<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Let's Do DevOps]]></title><description><![CDATA[DevOps + AI = <3. Trusted by readers from Amazon, Intel, HashiCorp, Boeing, GitHub, SAP, Fortinet, Sandia National Laboratories, Zscaler, Anduril]]></description><link>https://www.letsdodevops.com</link><image><url>https://substackcdn.com/image/fetch/$s_!WsHj!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbfbe0bf-9c66-4775-af63-3f2b58b03b80_1024x1024.png</url><title>Let&apos;s Do DevOps</title><link>https://www.letsdodevops.com</link></image><generator>Substack</generator><lastBuildDate>Mon, 15 Jun 2026 08:08:38 GMT</lastBuildDate><atom:link href="https://www.letsdodevops.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Kyler Middleton]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[letsdodevops@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[letsdodevops@substack.com]]></itunes:email><itunes:name><![CDATA[Kyler Middleton]]></itunes:name></itunes:owner><itunes:author><![CDATA[Kyler Middleton]]></itunes:author><googleplay:owner><![CDATA[letsdodevops@substack.com]]></googleplay:owner><googleplay:email><![CDATA[letsdodevops@substack.com]]></googleplay:email><googleplay:author><![CDATA[Kyler Middleton]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[🔥Designing Specialized Agents - Model Selection, Tool Scoping, and Output Contracts🔥]]></title><description><![CDATA[aka, here's your toolbox and marching orders, bot]]></description><link>https://www.letsdodevops.com/p/designing-specialized-agents-model</link><guid isPermaLink="false">https://www.letsdodevops.com/p/designing-specialized-agents-model</guid><dc:creator><![CDATA[Kyler Middleton]]></dc:creator><pubDate>Tue, 02 Jun 2026 14:40:34 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Zgqm!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab4f4c4d-2b19-439d-b638-368d531298b2_2096x554.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote><p><em>This blog series focuses on presenting complex DevOps projects as simple and approachable via plain language and lots of pictures. You can do it!</em></p><p>These articles are supported by readers, <a href="https://www.letsdodevops.com/subscribe">please consider subscribing</a> to support me writing more of these articles &lt;3 :)</p></blockquote><p>Hey all!</p><p>Last article we built the orchestrator pattern, a single CLAUDE.md file that coordinates five agents through a full development pipeline. If you missed it: </p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;1a1b0d23-7c58-4a1d-af0a-77285619334b&quot;,&quot;caption&quot;:&quot;This blog series focuses on presenting complex DevOps projects as simple and approachable via plain language and lots of pictures. You can do it!&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;&#128293;The Orchestrator Pattern - Turning CLAUDE.md Into an Autonomous Agent Team Pipeline&#128293;&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:87384289,&quot;name&quot;:&quot;Kyler Middleton&quot;,&quot;bio&quot;:&quot;Kyler been a systems, network, call center, and security engineer, and is now a DevOps and software engineer. She speaks at any conference that will have her, hosts Day Two DevOps podcast, and writes up cool projects on Medium. Let's Do DevOps!&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f35f1a97-6536-4ac6-9a92-506396c09217_1410x1410.jpeg&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2026-05-19T14:45:45.567Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!hptu!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F214adae8-c032-406e-a8dd-ffabcec27df1_1536x1024.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://www.letsdodevops.com/p/the-orchestrator-pattern-turning&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:198065105,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:0,&quot;comment_count&quot;:0,&quot;publication_id&quot;:2733228,&quot;publication_name&quot;:&quot;Let's Do DevOps&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!WsHj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbfbe0bf-9c66-4775-af63-3f2b58b03b80_1024x1024.png&quot;,&quot;belowTheFold&quot;:false,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p>That article covered the pipeline flow and how agents hand off work to each other. This one zooms in on how each agent is configured. The file configuration, what&#8217;s optional and what&#8217;s not, and the design decisions we made to make the autonomous development pipeline reliable.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Zgqm!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab4f4c4d-2b19-439d-b638-368d531298b2_2096x554.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Zgqm!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab4f4c4d-2b19-439d-b638-368d531298b2_2096x554.png 424w, https://substackcdn.com/image/fetch/$s_!Zgqm!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab4f4c4d-2b19-439d-b638-368d531298b2_2096x554.png 848w, https://substackcdn.com/image/fetch/$s_!Zgqm!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab4f4c4d-2b19-439d-b638-368d531298b2_2096x554.png 1272w, https://substackcdn.com/image/fetch/$s_!Zgqm!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab4f4c4d-2b19-439d-b638-368d531298b2_2096x554.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Zgqm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab4f4c4d-2b19-439d-b638-368d531298b2_2096x554.png" width="1456" height="385" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ab4f4c4d-2b19-439d-b638-368d531298b2_2096x554.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:385,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:133965,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.letsdodevops.com/i/200154875?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab4f4c4d-2b19-439d-b638-368d531298b2_2096x554.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Zgqm!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab4f4c4d-2b19-439d-b638-368d531298b2_2096x554.png 424w, https://substackcdn.com/image/fetch/$s_!Zgqm!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab4f4c4d-2b19-439d-b638-368d531298b2_2096x554.png 848w, https://substackcdn.com/image/fetch/$s_!Zgqm!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab4f4c4d-2b19-439d-b638-368d531298b2_2096x554.png 1272w, https://substackcdn.com/image/fetch/$s_!Zgqm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab4f4c4d-2b19-439d-b638-368d531298b2_2096x554.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Now lets talk how we can configure the sub-agents! </p><h1>Agent Files Are (Just) Markdown</h1><p>Each agent is defined in a single markdown file inside &#8220;.claude/agents/&#8221;, one file per agent. The Coder agent lives in &#8220;.claude/agents/coder.md&#8221;, the CodeReviewer in &#8220;.claude/agents/code-reviewer.md&#8221;, and so on.</p><p>The real config is in the YAML frontmatter block at the top. Here&#8217;s what the CodeReviewer&#8217;s looks like:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">---
name: code-reviewer
description: Code review specialist that reviews all Python and Terraform 
  changes. Enforces quality standards, catches bugs, and reports issues. 
  Does NOT fix code - reports findings for the main agent to delegate 
  to a Coder.
tools: Read, Glob, Grep, Bash
model: opus
---</code></pre></div><p>Four required fields do all the work:</p>
      <p>
          <a href="https://www.letsdodevops.com/p/designing-specialized-agents-model">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[🔥The Orchestrator Pattern - Turning CLAUDE.md Into an Autonomous Agent Team Pipeline🔥]]></title><description><![CDATA[aka, make robots do the damn sdlc]]></description><link>https://www.letsdodevops.com/p/the-orchestrator-pattern-turning</link><guid isPermaLink="false">https://www.letsdodevops.com/p/the-orchestrator-pattern-turning</guid><dc:creator><![CDATA[Kyler Middleton]]></dc:creator><pubDate>Tue, 19 May 2026 14:45:45 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!hptu!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F214adae8-c032-406e-a8dd-ffabcec27df1_1536x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote><p><em>This blog series focuses on presenting complex DevOps projects as simple and approachable via plain language and lots of pictures. You can do it!</em></p><p>These articles are supported by readers, <a href="https://www.letsdodevops.com/subscribe">please consider subscribing</a> to support me writing more of these articles &lt;3 :)</p></blockquote><p>Hey all,</p><p>In the last series I wrote for LDD, we built an agentic Slack bot on AWS AgentCore. The architecture, MCP tools, memory, deployment. If you missed it, start here: </p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;8358e324-35ad-4ccb-b13f-e8fb1120ede3&quot;,&quot;caption&quot;:&quot;This blog series focuses on presenting complex DevOps projects as simple and approachable via plain language and lots of pictures. You can do it!&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;AWS AgentCore Agentic Slack Bot - Full Architecture and Code (1/4)&#128293;&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:87384289,&quot;name&quot;:&quot;Kyler Middleton&quot;,&quot;bio&quot;:&quot;Kyler been a systems, network, call center, and security engineer, and is now a DevOps and software engineer. She speaks at any conference that will have her, hosts Day Two DevOps podcast, and writes up cool projects on Medium. Let's Do DevOps!&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f35f1a97-6536-4ac6-9a92-506396c09217_1410x1410.jpeg&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2026-02-03T16:46:19.456Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!7OeN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc30ca40e-0ac0-4089-8737-07e7f9c59a6f_1258x1058.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://www.letsdodevops.com/p/aws-agentcore-agentic-slack-bot-full&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:186750859,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:7,&quot;comment_count&quot;:0,&quot;publication_id&quot;:2733228,&quot;publication_name&quot;:&quot;Let's Do DevOps&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!WsHj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbfbe0bf-9c66-4775-af63-3f2b58b03b80_1024x1024.png&quot;,&quot;belowTheFold&quot;:false,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p>That series ended with a teaser about multi-agent orchestration. This is that series. We&#8217;re going to teach Claude Code to develop the bot itself. Implement features, review its own code, deploy to DEV, and test the live bot via Slack. All from a single prompt.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!hptu!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F214adae8-c032-406e-a8dd-ffabcec27df1_1536x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!hptu!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F214adae8-c032-406e-a8dd-ffabcec27df1_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!hptu!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F214adae8-c032-406e-a8dd-ffabcec27df1_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!hptu!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F214adae8-c032-406e-a8dd-ffabcec27df1_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!hptu!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F214adae8-c032-406e-a8dd-ffabcec27df1_1536x1024.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!hptu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F214adae8-c032-406e-a8dd-ffabcec27df1_1536x1024.png" width="1456" height="971" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/214adae8-c032-406e-a8dd-ffabcec27df1_1536x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:971,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1660746,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.letsdodevops.com/i/198065105?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F214adae8-c032-406e-a8dd-ffabcec27df1_1536x1024.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!hptu!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F214adae8-c032-406e-a8dd-ffabcec27df1_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!hptu!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F214adae8-c032-406e-a8dd-ffabcec27df1_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!hptu!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F214adae8-c032-406e-a8dd-ffabcec27df1_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!hptu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F214adae8-c032-406e-a8dd-ffabcec27df1_1536x1024.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">ChatGPT has gotten so SNAZZY since I used it last, these diagrams are amazing</figcaption></figure></div><p>This first article covers the orchestrator pattern: how one CLAUDE.md file coordinates five agents through a full development pipeline, and how we codified our entire SDLC into config files that Claude Code follows autonomously.</p><p>If you want to skip the words and see the code, go here: </p><blockquote><p><a href="https://github.com/KyMidd/AgentCore_AgenticSlackBot/blob/master/CLAUDE.md">Orchestrator claude.md</a></p><p><a href="https://github.com/KyMidd/AgentCore_AgenticSlackBot/tree/master/.claude/agents">Specialized Agent files</a></p></blockquote>
      <p>
          <a href="https://www.letsdodevops.com/p/the-orchestrator-pattern-turning">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[🔥Sending Email from Claude Code with Zero Dependencies🔥]]></title><description><![CDATA[aka, curl, awk, and an email walk into a bar]]></description><link>https://www.letsdodevops.com/p/sending-email-from-claude-code-with</link><guid isPermaLink="false">https://www.letsdodevops.com/p/sending-email-from-claude-code-with</guid><dc:creator><![CDATA[Alex Moore]]></dc:creator><pubDate>Tue, 05 May 2026 14:31:23 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!rXiL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F184a24ba-58de-4e45-9899-90e9d8aff7f1_1912x1216.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote><p><em>This blog series focuses on presenting complex DevOps projects as simple and approachable via plain language and lots of pictures. If you find these useful, please consider subscribing. It&#8217;s what keeps these articles coming. &lt;3</em></p></blockquote><p>Hey all! I&#8217;ve been building an agentic email bot on AWS Bedrock AgentCore that reads and replies to emails autonomously. I wrote up that whole series:</p><ul><li><p><a href="https://www.letsdodevops.com/p/agentic-ai-email-bot-on-aws-bedrock">Part 1: Full Architecture and O365 Integration</a> - EventBridge polling, Graph API delta queries, and handing off to AgentCore</p></li><li><p><a href="https://www.letsdodevops.com/p/agentic-ai-email-bot-on-aws-bedrock-05d">Part 2: Building the Thread, Sending the Reply</a> - Conversation context, email-safe HTML rendering, and production operations</p></li></ul><p>While building it, I wired up Microsoft Graph API for O365 access so I could have my Claude Agent test and verify changes during development. That Graph integration became the foundation for something more immediately useful: a Claude Code skill that lets you send email directly from a conversation.</p><p>Later, in a Slack channel dedicated to using AI, someone asked if anyone had a solution to allow Claude Code to send emails, specifically for automations like emailing reports on a schedule. So I pulled it out, made it portable, and published it to our internal plugin marketplace.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!rXiL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F184a24ba-58de-4e45-9899-90e9d8aff7f1_1912x1216.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!rXiL!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F184a24ba-58de-4e45-9899-90e9d8aff7f1_1912x1216.png 424w, https://substackcdn.com/image/fetch/$s_!rXiL!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F184a24ba-58de-4e45-9899-90e9d8aff7f1_1912x1216.png 848w, https://substackcdn.com/image/fetch/$s_!rXiL!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F184a24ba-58de-4e45-9899-90e9d8aff7f1_1912x1216.png 1272w, https://substackcdn.com/image/fetch/$s_!rXiL!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F184a24ba-58de-4e45-9899-90e9d8aff7f1_1912x1216.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!rXiL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F184a24ba-58de-4e45-9899-90e9d8aff7f1_1912x1216.png" width="1456" height="926" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/184a24ba-58de-4e45-9899-90e9d8aff7f1_1912x1216.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:926,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Claude Code sending an email via the send-o365-email skill&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Claude Code sending an email via the send-o365-email skill" title="Claude Code sending an email via the send-o365-email skill" srcset="https://substackcdn.com/image/fetch/$s_!rXiL!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F184a24ba-58de-4e45-9899-90e9d8aff7f1_1912x1216.png 424w, https://substackcdn.com/image/fetch/$s_!rXiL!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F184a24ba-58de-4e45-9899-90e9d8aff7f1_1912x1216.png 848w, https://substackcdn.com/image/fetch/$s_!rXiL!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F184a24ba-58de-4e45-9899-90e9d8aff7f1_1912x1216.png 1272w, https://substackcdn.com/image/fetch/$s_!rXiL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F184a24ba-58de-4e45-9899-90e9d8aff7f1_1912x1216.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Claude Code sending an email via the send-o365-email skill</figcaption></figure></div><p>There are a lot of good reasons to want this. You might be testing email-related functionality, like I was, or you want to say &#8220;email Sarah and update her on our progress with this project&#8221; and have Claude just handle it in context. Or you&#8217;re running scheduled jobs and need Claude to fire off a notification or send a report when something completes. Once you have it, you find uses for it constantly. (I also use the email access in general to leverage Claude in a custom daily review process to help me stay on top of tasks and such, but that&#8217;s a story for another blog post.)</p><p>The idea sounds simple enough. The implementation had some genuinely interesting constraints that made it worth writing up.</p>
      <p>
          <a href="https://www.letsdodevops.com/p/sending-email-from-claude-code-with">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[🔥Agentic AI Email Bot on AWS Bedrock AgentCore: Building the Thread, Sending the Reply, and Operating in Production (2/2)🔥]]></title><description><![CDATA[aka, turns out email-safe HTML is basically a different language]]></description><link>https://www.letsdodevops.com/p/agentic-ai-email-bot-on-aws-bedrock-05d</link><guid isPermaLink="false">https://www.letsdodevops.com/p/agentic-ai-email-bot-on-aws-bedrock-05d</guid><dc:creator><![CDATA[Alex Moore]]></dc:creator><pubDate>Tue, 21 Apr 2026 15:47:53 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!14h3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F212ffcad-5777-406b-bbeb-ddf41e5d9a60_1694x1620.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote><p><em>This blog series focuses on presenting complex DevOps projects as simple and approachable via plain language and lots of pictures. If you find these useful, please consider subscribing - it&#8217;s what keeps these articles coming. &lt;3</em></p></blockquote><p>Hey all! Alex here again. In <a href="https://www.letsdodevops.com/p/agentic-ai-email-bot-on-aws-bedrock">Part 1</a> we covered the polling architecture: EventBridge, delta queries, idempotency, and handing off to AgentCore. That got us to the point where the AgentCore worker receives an email payload and needs to do something useful with it.</p><p>This article covers what happens next. The worker needs to understand the full conversation, not just the latest message. It needs to run the agent. And it needs to send a reply that actually looks good in Outlook, Gmail, and Apple Mail. Each of those is its own rabbit hole.</p><p>This is Part 2 of a 2-part series:</p><ul><li><p><a href="https://www.letsdodevops.com/p/agentic-ai-email-bot-on-aws-bedrock">Part 1</a>: Full architecture and Microsoft 365 integration</p></li><li><p>Part 2 (this article): Building the email thread, sending formatted replies, and operating in production</p></li></ul>
      <p>
          <a href="https://www.letsdodevops.com/p/agentic-ai-email-bot-on-aws-bedrock-05d">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[🔥Agentic AI Email Bot on AWS Bedrock AgentCore: Full Architecture and O365 Integration (1/2)🔥]]></title><description><![CDATA[aka, yes your inbox can have a robot in it now]]></description><link>https://www.letsdodevops.com/p/agentic-ai-email-bot-on-aws-bedrock</link><guid isPermaLink="false">https://www.letsdodevops.com/p/agentic-ai-email-bot-on-aws-bedrock</guid><dc:creator><![CDATA[Alex Moore]]></dc:creator><pubDate>Wed, 01 Apr 2026 14:31:39 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!xumB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0bb4c34d-bb71-4f00-9386-27369b88fc4d_871x346.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote><p><em>This blog series focuses on presenting complex DevOps projects as simple and approachable via plain language and lots of pictures. You can do it!</em></p><p>These articles are supported by readers, <a href="https://www.letsdodevops.com/subscribe">please consider subscribing</a> to support writing more of these articles &lt;3 :)</p></blockquote><p>Hey all! I&#8217;m Alex, a new contributor to Let&#8217;s Do DevOps. At work we&#8217;ve been building out a fleet of agentic AI bots backed by AWS Bedrock AgentCore: bots that live in Slack, Teams, and now email. Kyler has covered the Slack bot in depth across several articles. I want to show you the email version: same AgentCore foundation, completely different integration challenge.</p><p>The goal was to lower the friction for people to interact with our internal agentic bot. Slack and Teams work great if you&#8217;re in them all day, but not everyone is. The C-suite lives in email, and there are great email-native use cases too. Forward a vendor offer and ask the bot if accepting it would violate the company gift policy. Get CC&#8217;d on a question you don&#8217;t know the answer to? CC the bot and let it respond. The inbox is already where a lot of conversation happens, so we brought the bot there.</p><p>This is Part 1 of a 2-part series:</p><ul><li><p><strong>Part 1 (this article)</strong>: Full architecture and Microsoft 365 integration</p></li><li><p><a href="https://www.letsdodevops.com/p/agentic-ai-email-bot-on-aws-bedrock-05d">Part 2</a>: Building the email thread, sending formatted replies, and operating in production</p></li></ul><h2><strong>The Problem: Email Is Not Slack</strong></h2><p>If you&#8217;ve built a Slack bot before, you know how it works. Slack sends you an event when someone messages the bot. You process it, respond, done. It&#8217;s push-based so Slack does the work of notifying you.</p><p>Email is more complicated.</p><p>While Microsoft Graph supports push notifications for mail via a subscription, those subscriptions expire after 7 days and have to be renewed, you have to handle a validation handshake on creation, and if your endpoint is slow or unavailable, Graph will start dropping notifications permanently with no recovery. It&#8217;s more moving parts than it looks.</p><p>We chose polling instead. Simpler to operate, no subscription lifecycle to manage, and the delta query pattern makes it efficient.</p><p>That choice shapes the whole architecture. Instead of an event-driven Lambda that wakes up when Graph pings it, you need a polling layer that runs on a schedule, checks for new messages, and decides what to do with them.</p><p>But once you have polling, you immediately have two new problems: efficiency (you don&#8217;t want to re-fetch every email every minute) and idempotency (you don&#8217;t want to process the same email twice if something goes wrong).</p><p>We solved both. Let me show you how.</p>
      <p>
          <a href="https://www.letsdodevops.com/p/agentic-ai-email-bot-on-aws-bedrock">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[AgentCore: Deployment, Operations, and Lessons Learned (4/4) 🔥]]></title><description><![CDATA[aka, wish I'd know that earlier]]></description><link>https://www.letsdodevops.com/p/agentcore-deployment-operations-and</link><guid isPermaLink="false">https://www.letsdodevops.com/p/agentcore-deployment-operations-and</guid><dc:creator><![CDATA[Kyler Middleton]]></dc:creator><pubDate>Sun, 15 Mar 2026 23:22:55 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!2z7s!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1179c17e-bbd5-4f18-ba95-074787b3f721_1280x853.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote><p><em>This blog series focuses on presenting complex DevOps projects as simple and approachable via plain language and lots of pictures. You can do it!</em></p><p>These articles are supported by readers, <a href="https://www.letsdodevops.com/subscribe">please consider subscribing</a> to support me writing more of these articles &lt;3 :)</p></blockquote><p>Hey all!</p><p>In this 4th and last article in the AgentCore series, we&#8217;ll talk about real life deployment, operations, and lessons learned while building this project. Here are links to the other articles in the series: </p><ul><li><p>Part 1 - <a href="https://www.letsdodevops.com/p/aws-agentcore-agentic-slack-bot-full">AgentCore: Full Architecture and Code</a></p></li><li><p>Part 2 - <a href="https://www.letsdodevops.com/p/aws-agentcore-agentic-slack-bot-full-a98">AgentCore MCP Gateway</a></p></li><li><p>Part 3 - <a href="https://www.letsdodevops.com/p/aws-agentcore-agentic-slack-bot-agentcore">AgentCore Memory and Tools</a></p></li><li><p>Part 4 (this article) - AgentCore Deployment, Operations, and Lessons Learned</p></li></ul><p>Deploying an AgentCore runtime isn&#8217;t like deploying a Lambda function. There&#8217;s a container to build, a two-Lambda architecture to wire up, module boundaries to get right in Terraform, and a regional split that will trip you up if you&#8217;re not expecting it. Once it&#8217;s running, the operational model is different too. Your bot is a long-lived application now, not a function that spins up and dies. That changes how you think about health checks, debugging, cost, and failure modes.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!2z7s!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1179c17e-bbd5-4f18-ba95-074787b3f721_1280x853.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!2z7s!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1179c17e-bbd5-4f18-ba95-074787b3f721_1280x853.jpeg 424w, https://substackcdn.com/image/fetch/$s_!2z7s!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1179c17e-bbd5-4f18-ba95-074787b3f721_1280x853.jpeg 848w, https://substackcdn.com/image/fetch/$s_!2z7s!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1179c17e-bbd5-4f18-ba95-074787b3f721_1280x853.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!2z7s!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1179c17e-bbd5-4f18-ba95-074787b3f721_1280x853.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!2z7s!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1179c17e-bbd5-4f18-ba95-074787b3f721_1280x853.jpeg" width="1280" height="853" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1179c17e-bbd5-4f18-ba95-074787b3f721_1280x853.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:853,&quot;width&quot;:1280,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:225183,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.letsdodevops.com/i/190873495?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1179c17e-bbd5-4f18-ba95-074787b3f721_1280x853.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!2z7s!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1179c17e-bbd5-4f18-ba95-074787b3f721_1280x853.jpeg 424w, https://substackcdn.com/image/fetch/$s_!2z7s!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1179c17e-bbd5-4f18-ba95-074787b3f721_1280x853.jpeg 848w, https://substackcdn.com/image/fetch/$s_!2z7s!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1179c17e-bbd5-4f18-ba95-074787b3f721_1280x853.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!2z7s!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1179c17e-bbd5-4f18-ba95-074787b3f721_1280x853.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This article walks through the Terraform patterns that hold everything together, what container lifecycle actually looks like in practice, how to debug when things go wrong, and an honest look at what I&#8217;d do differently if I started over. If you&#8217;ve been following along and thinking about building your own AgentCore agent, this is the practical stuff you&#8217;ll want before you start.</p><p>Let&#8217;s get into it.</p><h1>Terraform Deployment Patterns</h1><p>The whole project is structured as Terraform modules. Each component is its own module with its own variables, IAM roles, and resources. The root `main.tf` wires them together:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">module &#8220;gateway&#8221;    { source = &#8220;./gateway&#8221;    }  # MCP Gateway + Cognito
module &#8220;receiver&#8221;   { source = &#8220;./receiver&#8221;   }  # Slack webhook handler
module &#8220;invoker&#8221;    { source = &#8220;./invoker&#8221;    }  # Async AgentCore caller
module &#8220;worker&#8221;     { source = &#8220;./worker&#8221;     }  # AgentCore runtime
module &#8220;auth_portal&#8221; { source = &#8220;./auth_portal&#8221; }  # Per-user OAuth flows</code></pre></div><p>Data flows one direction: gateway outputs feed into the worker, the worker&#8217;s runtime ARN feeds into the invoker, and the invoker&#8217;s function name feeds into the receiver. Each module only gets the permissions and configuration it needs.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext"># main.tf - wiring it together
module &#8220;invoker&#8221; {
  source            = &#8220;./invoker&#8221;
  agent_runtime_arn = module.worker.runtime_arn  # Worker feeds invoker
}
module &#8220;receiver&#8221; {
  source                = &#8220;./receiver&#8221;
  invoker_function_name = module.invoker.invoker_function_name  # Invoker feeds receiver
}
module &#8220;worker&#8221; {
  source            = &#8220;./worker&#8221;
  gateway_arn       = module.gateway.gateway_arn       # Gateway feeds worker
  gateway_client_id = module.gateway.cognito_client_id
  gateway_url       = module.gateway.gateway_url
  memory_id         = aws_bedrockagentcore_memory.vera_memory.id
  # ... 20+ more variables
}</code></pre></div><p>That worker module has a lot of inputs. Gateway auth, memory config, knowledge base IDs, guardrail IDs, OAuth table references, audit log settings. It all gets passed as environment variables on the AgentCore runtime resource. This is the glue between infrastructure and application code. The worker&#8217;s Python code reads `os.environ.get(&#8221;GATEWAY_CLIENT_ID&#8221;)` and never knows or cares that Terraform set it.</p><p>The two Lambdas are deliberately minimal. The receiver is a single Python file zipped and deployed directly, no Docker, no build pipeline. 512MB memory, 10-second timeout, ARM64. Its only job is to validate the Slack signature and asynchronously invoke the invoker. The invoker is even smaller, 256MB, and its only job is to call `InvokeAgentRuntime`. Both run on ARM64 for the cost savings since Graviton Lambda is about 20% cheaper than x86.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext"># Receiver: tiny, fast, validates Slack webhooks
resource &#8220;aws_lambda_function&#8221; &#8220;receiver&#8221; {
  memory_size   = 512
  timeout       = 10
  runtime       = &#8220;python3.12&#8221;
  architectures = [&#8221;arm64&#8221;]
}
# Invoker: even tinier, calls AgentCore
resource &#8220;aws_lambda_function&#8221; &#8220;invoker&#8221; {
  memory_size   = 256
  timeout       = 900  # Matches Lambda max, waits for AgentCore
  runtime       = &#8220;python3.12&#8221;
  architectures = [&#8221;arm64&#8221;]
}</code></pre></div><p>The invoker&#8217;s 900-second timeout might look surprising. It&#8217;s set to the Lambda maximum because it&#8217;s making a synchronous call to AgentCore and waiting for the response. The agent can take minutes on complex queries. If the invoker times out before the agent finishes, the response gets lost. Setting it to max is cheap insurance since the invoker uses almost no memory and you only pay for execution time.</p><p>The worker container build is where things get more interesting. We use a content-hash tagging strategy so the container only rebuilds when the code actually changes:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">locals {
  # Hash all Python source files
  python_files_hash = sha256(join(&#8221;&#8220;, [
    for f in fileset(&#8221;${path.module}/src&#8221;, &#8220;*.py&#8221;) :
    filesha256(&#8221;${path.module}/src/${f}&#8221;)
  ]))
  # First 12 chars of hash as image tag
  image_tag = substr(local.python_files_hash, 0, 12)
  image_uri = &#8220;${var.ecr_repository_url}:${local.image_tag}&#8221;
}</code></pre></div><p>Every Python file, the Dockerfile, and `requirements.txt` are hashed. If nothing changes, the tag stays the same and `null_resource.build_and_push_worker_image` doesn&#8217;t fire. If you change a single line of Python, you get a new hash, a new tag, and a new build. No &#8220;latest&#8221; tag guessing games, no manual version bumps. Terraform knows exactly when to rebuild.</p><p>The build itself targets ARM64 using `docker buildx`:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">provisioner &#8220;local-exec&#8221; {
  command = &lt;&lt;EOF
    docker buildx build \
      --platform linux/arm64 \
      --output=type=registry,compression=gzip,force-compression=true \
      -t ${local.image_uri} \
      --push .
  EOF</code></pre></div><p>ARM64 containers on AgentCore use Graviton instances, same cost savings as the Lambdas. The `--output=type=registry` pushes directly to ECR without storing a local image, which keeps CI/CD clean.</p><p>One thing that caught me off guard: the regional split. AgentCore, memory, guardrails, and the gateway all run in us-east-1. But the Bedrock Knowledge Base is in us-west-2. At the time I set this up, not all Bedrock features were available in every region. The knowledge base needed a region where the embedding model was available, and that was us-west-2. The worker handles this with a separate region variable:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext"># providers.tf - two regions
provider &#8220;aws&#8221; {
  region = var.region  # us-east-1: AgentCore, gateway, memory
}
provider &#8220;aws&#8221; {
  alias  = &#8220;west2&#8221;
  region = &#8220;us-west-2&#8221;  # Knowledge Base only
}</code></pre></div><p>It works, but it means IAM policies need to cover both regions, and you need to be deliberate about which provider each resource uses. If you&#8217;re starting fresh, check if your region supports everything first. A single-region deployment is much simpler.</p><h1>Container Lifecycle and Operational Reality</h1><p>In Article 1, I talked about AgentCore&#8217;s &#8220;living application&#8221; model in the abstract. Now that I&#8217;ve been running it in production for a few months, here&#8217;s what it actually looks like.</p><p>The lifecycle is controlled by two values:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">variable &#8220;idle_timeout&#8221; {
  description = &#8220;Idle runtime session timeout in seconds&#8221;
  type        = number
  default     = 900   # 15 minutes
}
variable &#8220;max_lifetime&#8221; {
  description = &#8220;Maximum runtime lifetime in seconds&#8221;
  type        = number
  default     = 28800 # 8 hours
}</code></pre></div><p>`idle_timeout` is how long the container sits idle before AWS shuts it down. `max_lifetime` is the absolute ceiling, no matter how busy it is. After 8 hours, the container gets recycled and the next request spins up a fresh one.</p><p>The 15-minute idle timeout might seem short, but it&#8217;s a balance between responsiveness and cost. During business hours, Vera gets enough traffic to stay warm. After hours, the container shuts down within 15 minutes and you stop paying. The next morning&#8217;s first message takes a cold start penalty, about 30-45 seconds while the container boots, loads dependencies, fetches secrets, initializes MCP connections, and registers with AgentCore. After that, requests are fast.</p><p>Here&#8217;s the thing about cold starts though. With Lambda, a cold start was maybe 5-10 seconds for a lightweight function. AgentCore&#8217;s cold start is heavier because you&#8217;re booting a full Docker container with Python, MCP servers, Node.js (for Azure MCP), the OpenTelemetry instrumentation, and the AWS CLI MCP server. Look at the Dockerfile and you can see why it takes a bit:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;dockerfile&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-dockerfile"># System deps: Node.js for Azure MCP, .NET libs, curl, etc.
RUN apt-get update &amp;&amp; apt-get install -y \
    ca-certificates curl tar gzip nodejs npm \
    libicu-dev libssl3 zlib1g libgcc-s1 libstdc++6 libc6
# Azure MCP server
RUN npm install -g @azure/mcp@0.5.8
# Python dependencies
RUN pip install --no-cache-dir -r requirements.txt
# AWS CLI MCP server in its own venv
RUN mkdir -p /opt/aws-cli-mcp-server &amp;&amp; \
    cd /opt/aws-cli-mcp-server &amp;&amp; \
    uv venv &amp;&amp; \
    uv pip install --python .venv/bin/python awslabs.aws-api-mcp-server</code></pre></div><p>That&#8217;s a lot of stuff in one container. Vera talks to six or seven different platforms, and each one has its own runtime dependencies. Having these all ready to go means each reqeust is faster. </p><p>Once the container is warm, the application loop is straightforward. The `BedrockAgentCoreApp` handles health checks and request routing. When a request comes in, it hits the `@app.entrypoint` handler:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">@app.entrypoint
def handle_slack_message(payload):
    # Start a background thread so we don&#8217;t block health checks
    task_id = app.add_async_task(&#8221;slack_message_processing&#8221;, {...})
    def process_in_background():
        try:
            handle_message_event(...)
        except Exception as error:
            # Post error to Slack so users aren&#8217;t left hanging
            slack_app.client.chat_postMessage(
                channel=channel_id, text=error_message, thread_ts=thread_ts
            )
        finally:
            app.complete_async_task(task_id)
    thread = threading.Thread(target=process_in_background, daemon=True)
    thread.start()
    return {&#8221;status&#8221;: &#8220;processing&#8221;, &#8220;task_id&#8221;: task_id}</code></pre></div><p>I didn&#8217;t want to do background threads - they were required by AgentCore&#8217;s architecture. My lambda-built design was built to handle a single thread at a time, because who cares, each request uses a single lambda. There&#8217;s no need to have background threads and multi-tenancy, because... well, there isn&#8217;t multi-tenancy. That isn&#8217;t the case here. The blocking processing of a thread meant the heartbeat that AWS AgentCore sends to our application stops responding, and AgentCore thinks our application has died, and AgentCore&#8217;s service recycles the app - not great. </p><p>A background thread has a great benefit - it&#8217;s worked asynchronously, and the heartbeat process can continue responding even when working many requests. </p><p>Now, the cost question. Lambda is pay-per-invocation. You pay for exactly the compute time your function uses. AgentCore is pay-per-container-hour. You pay for the container running, whether it&#8217;s processing a request or sitting idle.</p><p>For a bot like Vera with steady weekday traffic, the costs end up similar. During business hours, the container is warm and handling requests efficiently. After hours and weekends, it shuts down after 15 minutes of inactivity. You&#8217;re not paying for 24/7 uptime.</p><p>Where Lambda wins is low-traffic bots. If your bot handles 5 requests a day, Lambda&#8217;s pay-per-invocation model is cheaper than keeping a container warm. AgentCore&#8217;s idle timeout helps, but there&#8217;s still the overhead of cold starting multiple times a day.</p><p>For Vera&#8217;s usage pattern of hundreds of users, dozens of requests per hour during business hours, and near-zero after hours, the cost is roughly the same. I didn&#8217;t migrate for cost savings. I migrated for the features.</p><h1>Debugging and Observability</h1><p>Debugging in AgentCore was a HUGE PAIN. When I first migrated, I couldn&#8217;t get the stdout messages to appear in cloudwatch at all. I thought I had to depend on x-ray and OpenTelemetry (OTEL), and that situation is just horrible. I wanted to like the OTEL observability stack and how it&#8217;s currently implemented in AWS but I just can&#8217;t. Clicking through literally thousands of events to find the failure straight up sucks. </p><p>After debugging for hours, I found out that python BUFFERS ITS OUTPUT to cloudwatch, sometimes for the entire duration of the container, and then it dies without writing to cloudwatch. </p><p>Adding the `python -u` to the invocation of the app makes it logs to stdout without buffering. This has a (very tiny) impact on perforamcne, but it means that the APP GENERATES LOGS which are absolutely necessary to run any damn thing. </p><p>The OpenTelemetry setup is one line in the Dockerfile:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;dockerfile&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-dockerfile">CMD [&#8221;opentelemetry-instrument&#8221;, &#8220;python&#8221;, &#8220;-u&#8221;, &#8220;-m&#8221;, &#8220;worker_agentcore&#8221;]</code></pre></div><p>That `opentelemetry-instrument` wrapper auto-instruments boto3 calls, HTTP requests, and other common libraries. You get traces in X-Ray showing the full lifecycle of a request: Bedrock Converse API calls, Secrets Manager fetches, MCP tool invocations, Slack API posts. When a request takes 4 minutes and you want to know why, X-Ray shows you which tool call was slow.</p><p>I don&#8217;t like to use .logging(), I literally just have a `DEBUG_ENABLED` environment variable that turns on stdout print() logging. In production it&#8217;s off, but when something weird happens, you can flip it on and get full request/response bodies logged:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">debug_enabled = os.environ.get(&#8221;DEBUG_ENABLED&#8221;, &#8220;False&#8221;)
# In the Converse API call
if debug_enabled == &#8220;True&#8221;:
    print(f&#8221;converse_body: {converse_body}&#8221;)</code></pre></div><p>Error handling follows the same philosophy of telling users what happened. Vera categorizes errors into three buckets and gives specific guidance for each:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">THROTTLING_KEYWORDS = [&#8221;throttl&#8221;, &#8220;rate limit&#8221;, &#8220;too many requests&#8221;, ...]
CONTEXT_OVERFLOW_KEYWORDS = [&#8221;context window&#8221;, &#8220;too many tokens&#8221;, ...]
if is_context_overflow:
    return &#8220;Request was too large... try narrowing your request&#8221;
elif is_throttling:
    return &#8220;High demand, please try again in a few minutes&#8221;
else:
    return &#8220;Unexpected error, please try again&#8221;</code></pre></div><p>Context window overflows happen more often than you&#8217;d expect. When Vera analyzes a large Jira board with hundreds of tickets, the accumulated tool responses can blow past the model&#8217;s context limit. The error message tells users to narrow their request, like limiting to a specific sprint or date range, which has cut down on repeat failures.</p><p>The Strands SDK quirk I mentioned in the memory article also shows up in debugging. Short-term memory events that contain malformed or empty messages cause the Strands memory interface to crash rather than gracefully discarding them. The workaround is to use unique session IDs per message so that corrupted short-term memory from one conversation can&#8217;t bleed into the next. It&#8217;s not elegant, but it&#8217;s reliable. If you see mysterious crashes during message processing with no obvious cause, check your session ID strategy first.</p><p>This bug will likely be fixed in the Strands Memory interfact client soon, but until then you can remove conversation turns that are empty which MCP tools sometimes generate, then memory didn&#8217;t crash. It only took me 5 hours of debugging to figure that out. </p><h1>What I&#8217;d Do Differently and What&#8217;s Next</h1><p>I&#8217;ve been running Vera on AgentCore in production for a few months now, serving hundreds of users across Slack, Teams, and Email. </p><p>Things I&#8217;d do the same: the module-per-component Terraform structure, the hash-based image tagging, the background thread pattern, ARM64 everywhere, and the three-Lambda architecture (despite this being quirky as all get out, it works great). These patterns have held up well and I&#8217;d use them again on a new project without hesitation.</p><p>Things I&#8217;d do differently:</p><p>Start with a single region. The us-east-1/us-west-2 split was necessary at the time because of Bedrock feature availability, but it added complexity to every IAM policy and every debugging session. Check regional availability before you commit to a multi-region setup. If you can fit everything in one region, do it.</p><p>Don&#8217;t underestimate the Dockerfile. Every MCP server you add brings its own runtime: Node.js for Azure, a Python venv for AWS CLI, pip packages for everything else. The Dockerfile grew fast and so did the cold start time. If I were starting fresh, I&#8217;d think harder about which MCP servers truly need to be local (stdio) versus remote, because every local server is another dependency baked into your container image.</p><p>Now, what&#8217;s next. AgentCore supports multi-agent orchestration, where a supervisor agent delegates to specialist agents, each with their own tools and context windows. I haven&#8217;t implemented this yet, but the architecture is clearly heading there. Imagine a &#8220;manager&#8221; agent that routes questions to a GitHub specialist, a Jira specialist, and an incident response specialist, each optimized for their domain. The pieces are all there in AgentCore. I just haven&#8217;t needed it yet because Vera handles the current workload as a single agent.</p><p>The MCP Gateway is also evolving. When AWS adds broader bearer token support for gateway targets, the direct MCP connections for GitHub, Atlassian, and Splunk can all move to the gateway. That means less auth code in the application, fewer secrets to manage, and adding a new tool provider becomes a Terraform resource instead of a code change.</p><p>If you&#8217;re considering building an agentic bot on AWS today: if your workflows fit within Lambda&#8217;s 15-minute timeout and you don&#8217;t need persistent memory or a managed tool gateway, Lambda with the Strands SDK is simpler and cheaper. It works great. But if you&#8217;re hitting the timeout wall, or you want memory that persists across conversations, or you&#8217;re tired of managing credentials for six different MCP servers in application code, AgentCore is worth the migration.</p><p>The code for this entire project is open source: https://github.com/KyMidd/AgentCore_AgenticSlackBot. Feel free to poke around, steal ideas, or open issues when things don&#8217;t make sense.</p><p>Happy building!<br>kyler</p>]]></content:encoded></item><item><title><![CDATA[AgentCore: Memory and Tools (3/4)🔥]]></title><description><![CDATA[aka, be a teammate, not just a chat bot]]></description><link>https://www.letsdodevops.com/p/aws-agentcore-agentic-slack-bot-agentcore</link><guid isPermaLink="false">https://www.letsdodevops.com/p/aws-agentcore-agentic-slack-bot-agentcore</guid><dc:creator><![CDATA[Kyler Middleton]]></dc:creator><pubDate>Tue, 03 Mar 2026 15:30:32 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!lSZG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71a51321-bd43-476e-965f-5c24e4214e26_1280x853.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote><p><em>This blog series focuses on presenting complex DevOps projects as simple and approachable via plain language and lots of pictures. You can do it!</em></p><p>These articles are supported by readers, <a href="https://www.letsdodevops.com/subscribe">please consider subscribing</a> to support me writing more of these articles &lt;3 :)</p></blockquote><p>Hey all!</p><p>In the last article, we covered how Vera connects to tool providers through the MCP Gateway and direct connections. Now let&#8217;s talk about something that makes AgentCore feel genuinely different from Lambda: memory.</p><p>This article is part of a series: </p><ul><li><p>Part 1 - <a href="https://www.letsdodevops.com/p/aws-agentcore-agentic-slack-bot-full">AgentCore: Full Architecture and Code</a></p></li><li><p>Part 2 - <a href="https://www.letsdodevops.com/p/aws-agentcore-agentic-slack-bot-full-a98">AgentCore MCP Gateway</a></p></li><li><p>Part 3 (this article) - <a href="https://www.letsdodevops.com/p/aws-agentcore-agentic-slack-bot-agentcore">AgentCore Memory and Tools</a></p></li><li><p>Part 4 - <a href="https://www.letsdodevops.com/p/agentcore-deployment-operations-and">AgentCore Deployment, Operations, and Lessons Learned</a></p></li></ul><p>This is persistent memory that survives across conversations, across days, across sessions. Not only can users teach the bot to call them a nickname, users can teach Vera multi-step business processes: &#8220;When I ask for standup notes, check Jira for my tickets, pull my recent PRs from GitHub, and summarize my Confluence updates from the past week.&#8221; Once learned, Vera executes that workflow every time without the user repeating the instructions. In the first month of deployment, Vera memorized roughly 2,000 facts across our user base, including work patterns, team structures, preferred workflows, and procedural preferences.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!lSZG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71a51321-bd43-476e-965f-5c24e4214e26_1280x853.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!lSZG!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71a51321-bd43-476e-965f-5c24e4214e26_1280x853.jpeg 424w, https://substackcdn.com/image/fetch/$s_!lSZG!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71a51321-bd43-476e-965f-5c24e4214e26_1280x853.jpeg 848w, https://substackcdn.com/image/fetch/$s_!lSZG!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71a51321-bd43-476e-965f-5c24e4214e26_1280x853.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!lSZG!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71a51321-bd43-476e-965f-5c24e4214e26_1280x853.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!lSZG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71a51321-bd43-476e-965f-5c24e4214e26_1280x853.jpeg" width="1280" height="853" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/71a51321-bd43-476e-965f-5c24e4214e26_1280x853.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:853,&quot;width&quot;:1280,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:236413,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.letsdodevops.com/i/189390040?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71a51321-bd43-476e-965f-5c24e4214e26_1280x853.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!lSZG!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71a51321-bd43-476e-965f-5c24e4214e26_1280x853.jpeg 424w, https://substackcdn.com/image/fetch/$s_!lSZG!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71a51321-bd43-476e-965f-5c24e4214e26_1280x853.jpeg 848w, https://substackcdn.com/image/fetch/$s_!lSZG!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71a51321-bd43-476e-965f-5c24e4214e26_1280x853.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!lSZG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71a51321-bd43-476e-965f-5c24e4214e26_1280x853.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This is one of those features where the &#8220;learning AI&#8221; model really pays off. In Lambda, every invocation starts with a blank slate. You can bolt on memory by writing to DynamoDB or S3, but you&#8217;re building and maintaining that whole system yourself. AgentCore gives you a managed memory service with strategies, namespaces, and semantic search out of the box.</p><p>AgentCore memory has two layers that work together. Short-term memory events capture each conversation as it happens. A backend process then analyzes those events and extracts durable facts and preferences into long-term memory records. This article covers both layers, how we use the user preference strategy to give Vera per-user memory, the identity model that ties it all together, and the tools we built so users can see and control what&#8217;s been remembered about them.</p><p>Let&#8217;s get into it.</p><p>If you&#8217;d prefer to skip the write-up and just read the code, <a href="https://github.com/KyMidd/AgentCore_AgenticSlackBot">the entire codebase is public and MIT open sourced here</a>. </p><h1>Two Layers of Memory: Events and Records</h1><p>AgentCore memory has two distinct layers, and understanding the difference is important because they serve very different purposes.</p><p>Short-term memory (events) are created during each interaction with the bot. When a user sends a message and Vera responds, that exchange gets written as an event. Events are the raw material. They capture what happened in a specific conversation, and they expire based on your configured TTL (Usually 7 days or so). </p><p>Long-term memory (records) are created by AgentCore&#8217;s backend processing system based on the memory strategy/strategies you configure. After events are written, AgentCore asynchronously analyzes them and extracts durable facts, preferences, and patterns. These get stored as memory records in a namespace you define. Records are the refined output. They&#8217;re what the bot actually retrieves and uses in future conversations.</p><ul><li><p>Events (short-term): Created by each interaction. Contains raw conversation exchanges. Lifespan is a configurable TTL (we use 30 days).</p></li><li><p>Records (long-term): Created by the AgentCore backend. Contains extracted facts, preferences, and procedures. Lifespan is also a configurable TTL (we use 30 days).</p></li></ul><p>Here&#8217;s the flow in practice:</p><ol><li><p>A user tells Vera: &#8220;When I ask for standup notes, check Jira for my tickets and pull my recent GitHub PRs&#8221;</p></li><li><p>Vera writes that conversation as a short-term memory event</p></li><li><p>AgentCore&#8217;s backend asynchronously processes the event and extracts a long-term record: &#8220;User&#8217;s standup procedure: query Jira tickets, fetch GitHub PRs&#8221;</p></li><li><p>Days later, the user says: &#8220;Standup notes please&#8221;</p></li><li><p>Vera retrieves the relevant memory record, recognizes the learned workflow, and executes it</p></li></ol><p>You don&#8217;t (and can&#8217;t!) write long-term memories directly. You have conversations, events get created, and AgentCore&#8217;s backend figures out what&#8217;s worth remembering. Your job is to configure the strategy that tells AgentCore what kind of things to extract.</p><blockquote><p>Pro tip: It takes about a minute after the event is created for the record to be created. That&#8217;s usually fast enough, but I try to tell my users what to expect for the short turn-around time.</p></blockquote><h1>What Gets Remembered - Memory Strategies</h1><p>AgentCore supports multiple memory strategies that you can configure on the AWS Memory resource. Each strategy tells the backend what to look for when processing events. I&#8217;ve only explored one so far: user preferences. </p><p>User preferences extract facts about individual users. Things like timezone, preferred tools, communication style, team membership, and procedural workflows. The strategy is keyed by actor ID, so each user gets their own isolated memory space.</p><p>Here&#8217;s the Terraform:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml"># The memory resource itself - shared across all strategies
resource &#8220;aws_bedrockagentcore_memory&#8221; &#8220;vera_memory&#8221; {
  name                      = &#8220;${title(var.region_short_code)}${title(var.account_short_code)}VeraMemory&#8221;
  event_expiry_duration     = var.session_ttl_days
  memory_execution_role_arn = module.worker.worker_task_role_arn
}
# User preferences strategy
# Extracts facts, preferences, and patterns from conversations
# Distinct for each user (keyed by actorId)
resource &#8220;aws_bedrockagentcore_memory_strategy&#8221; &#8220;user_preferences&#8221; {
  name                      = &#8220;${title(var.region_short_code)}${title(var.account_short_code)}UserPreferences&#8221;
  memory_id                 = aws_bedrockagentcore_memory.vera_memory.id
  type                      = &#8220;USER_PREFERENCE&#8221;
  namespaces                = [&#8221;/preferences/{actorId}&#8221;]
  memory_execution_role_arn = module.worker.worker_task_role_arn
}</code></pre></div><p>Two things to notice:</p><p>The `event_expiry_duration` controls how long short-term events stick around. Events older than that get cleaned up automatically. This is the raw conversation data, and you probably don&#8217;t want it living forever.</p><p>The namespace pattern `/preferences/{actorId}` is where the magic happens. AgentCore replaces `{actorId}` with the actual user identifier at runtime, creating per-user memory isolation. My preferences live in `/preferences/kyler_middleton`. Someone else&#8217;s live in their own namespace. </p><blockquote><p>Users don&#8217;t have the ability to fetch memories for others users - that&#8217;d be a scary thing to deploy, and is something I had to be careful not to permit</p></blockquote><p>We plan to add a semantic knowledge strategy in the future. Where user preferences capture things specific to an individual (&#8221;my standup process&#8221;, &#8220;my timezone&#8221;), semantic knowledge would capture broader organizational facts that come up in conversations: &#8220;the deploy window is 2-4 PM EST&#8221; or &#8220;the staging environment uses us-west-2.&#8221; Knowledge that isn&#8217;t specific to a single user but is useful for the whole team. Adding it would mean a new strategy resource in Terraform with a different namespace path and including that namespace in the retrieval config.</p><h1>Identity: Sessions and Actor IDs</h1><p>Memory needs two identifiers to work correctly: a session ID that scopes short-term events, and an actor ID that scopes long-term records. Getting these right matters more than you&#8217;d think.</p><p>Here&#8217;s something that wasn&#8217;t obvious and cost me some debugging time. AgentCore uses the session ID to scope short-term memory. If you reuse the same session ID across multiple messages, the short-term memory accumulates every previous exchange. That sounds useful until you realize the agent replays all of that accumulated context on every new message, and it can corrupt the conversation state when messages from different contexts bleed together.</p><blockquote><p>This shouldn&#8217;t have been necessary, but the memory interfact for the Strands agentic library that Vera is based on has some limitations for processing blank or malformed messages, and crashes rather than discarding them. I might try and fix this in future, but for now I&#8217;m just pruning invalid messages and separate short term memory streams. </p></blockquote><p>I fixed it by giving every inbound message its own unique session ID.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">def generate_session_id(body):
    event = body.get(&#8221;event&#8221;, {})
    # Use the unique message timestamp from Slack
    ts = event.get(&#8221;ts&#8221;, &#8220;&#8221;)
    if ts:
        # Replace periods with underscores for AWS regex compliance
        return f&#8221;msg_{ts.replace(&#8217;.&#8217;, &#8216;_&#8217;)}&#8221;
    # Fallback to UUID if no timestamp
    import uuid
    return f&#8221;msg_{uuid.uuid4()}&#8221;</code></pre></div><p>Slack gives every message a unique timestamp like `1719432000.123456`. We convert that to `msg_1719432000_123456` (replacing periods with underscores because AWS&#8217;s regex validation doesn&#8217;t allow dots in session IDs).</p><p>This means every message is its own session from AgentCore&#8217;s perspective. Short-term memory doesn&#8217;t carry over between messages. But long-term memory absolutely does, because preferences are keyed by actor ID, not session ID.</p><p>And that&#8217;s the other half of the equation. The actor ID needs to be consistent across sessions. If I get actor ID `kyler_middleton` on Monday and `U12345678` on Tuesday, those memories live in different namespaces and the bot won&#8217;t remember Monday&#8217;s conversation.</p><p>We derive the actor ID from the user&#8217;s Slack profile name:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python"># Fetch the user&#8217;s Slack ID as fallback
actor_id = body[&#8221;event&#8221;][&#8221;user&#8221;]
# Extract real_name_normalized from Slack profile
real_name = (
    user_info_json.get(&#8221;user&#8221;, {})
    .get(&#8221;profile&#8221;, {})
    .get(&#8221;real_name_normalized&#8221;, &#8220;&#8221;)
)
if real_name:
    # Convert to lowercase and replace spaces with underscores
    actor_id = real_name.lower().replace(&#8221; &#8220;, &#8220;_&#8221;)</code></pre></div><p>&#8220;Kyler Middleton&#8221; becomes `kyler_middleton`. This has a nice side effect: the namespace path `/preferences/kyler_middleton` is human-readable when you&#8217;re debugging. But it&#8217;s not perfect. Someone with a name change, or a name that normalizes differently between Slack and Teams, would end up with split memories. For now it works, and the fallback to the raw Slack user ID (`U12345678`) means we never fail entirely.</p><blockquote><p>I use a &#8220;first_last&#8221; as the actor ID because it&#8217;s something I can correlate between Slack, Teams, and Email, so memory can be shared among all those platforms. And that is pretty darn cool. </p></blockquote><p>Both identifiers get packed into the memory config that flows through the rest of the system:</p><p>The design is stateless short-term memory with stateful long-term memory. Each message gets a fresh session (no short-term memory event accumulation), but user preferences persist across every session via the actor ID.</p><h1>Wiring Memory Into the Agent</h1><p>The memory config flows into the agent through the Strands SDK&#8217;s session manager. This is where you configure which namespaces to retrieve from and how selective the retrieval should be:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">from bedrock_agentcore.memory.integrations.strands.config import (
    AgentCoreMemoryConfig,
    RetrievalConfig,
)
from bedrock_agentcore.memory.integrations.strands.session_manager import (
    AgentCoreMemorySessionManager,
)
# Memory configuration
agentcore_config = AgentCoreMemoryConfig(
    memory_id=memory_config[&#8221;memory_id&#8221;],
    session_id=memory_config[&#8221;session_id&#8221;],
    actor_id=memory_config[&#8221;actor_id&#8221;],
    retrieval_config={
        # User preferences only - high relevance threshold
        &#8220;/preferences/{actorId}&#8221;: RetrievalConfig(
            top_k=5, relevance_score=0.7
        ),
    },
)
# Create session manager
session_manager = AgentCoreMemorySessionManager(
    agentcore_memory_config=agentcore_config, region_name=memory_region
)
# Pass to agent
agent = Agent(
    model=bedrock_model,
    system_prompt=system_prompt,
    tools=tools,
    session_manager=session_manager,
)</code></pre></div><p>The `retrieval_config` is a dictionary keyed by namespace pattern. Memory is just a vector-based knowledge base (just like every other bedrock knowledge base you&#8217;ll use), so for each namespace, we set two parameters:</p><p>`top_k=5` means retrieve the 5 most relevant memory records. You don&#8217;t want to flood the agent&#8217;s context with every memory ever stored. Five is usually enough to capture the important preferences without noise.</p><p>`relevance_score=0.7` is the minimum similarity threshold. Memory retrieval uses semantic search under the hood, so this filters out memories that are vaguely related but not actually useful. If you&#8217;re finding the bot isn&#8217;t retrieving memories it should, lower this. If it&#8217;s pulling in irrelevant ones, raise it.</p><p>The session manager handles everything automatically. When the agent starts processing a message, it retrieves relevant long-term memories and injects them into context. When the conversation ends, it writes events back to short-term memory. You don&#8217;t call any memory APIs in your agent logic. It just works.</p><h1>Giving Users Control: Memory Management Tools</h1><p>When I first rolled this out, someone asked if the bot could delete wrong long-term memories, and I promised it could. I didn&#8217;t realize that was a lie - the Strands bot natively *cannot* delete memory records. </p><p>To address users wanting to remove some or all memories, I wrote a few Strands memory tools that let users remove specific or all memories. </p><blockquote><p>I&#8217;m really loving strands tools. They&#8217;re not MCP based (which is a bummer) but they have deep integration into the user context of who launched them, so I can do sensitive things like fetch and delete memories. </p></blockquote><p>I built these tools: </p><ul><li><p>`list_my_memory_records` - A user can say &#8220;what do you remember about me?&#8221; and Vera will list every preference and fact that&#8217;s been extracted from their conversations.</p></li><li><p>`search_my_memories` - Runs semantic search against stored memories. &#8220;Do you remember my standup process?&#8221; will find the relevant record even if the stored text is worded differently.</p></li><li><p>`get_memory_record` - Retrieves the full details of a specific record by ID.</p></li><li><p>`delete_memory_record` - Removes a single memory. &#8220;Forget my standup workflow, I have a new process&#8221; will find and delete that specific record.</p></li><li><p>`delete_all_my_memories_in_namespace` - The nuclear option. This one has a two-step confirmation flow built into the tool itself:</p></li></ul><p>All of these tools are scoped to the current user&#8217;s namespace by default:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">default_namespace = f&#8221;/preferences/{actor_id}&#8221;</code></pre></div><p>The same actor ID that scopes memory retrieval also scopes the management tools.</p><h1>Summary and What&#8217;s Next</h1><p>This article walked through how AgentCore memory works at both layers: short-term events created by each conversation, and long-term records extracted asynchronously by the backend. We covered the user preference strategy and namespace isolation, how the Strands SDK session manager works, and the memory management tools that give users visibility and control over what the bot remembers.</p><p>Memory is the feature that transforms Vera from a tool into a teammate. The bot moves on from GenAI Q&amp;A bot to teaching Vera how they work, and Vera remembers. With 2,000 facts learned in the first month across nearly 500 users, it&#8217;s clear that people want to teach their bot. They just need the capability.</p><p>The code for this entire project is open source: https://github.com/KyMidd/AgentCore_AgenticSlackBot. Feel free to poke around, steal ideas, or open issues when things don&#8217;t make sense.</p><p>Happy building!<br>kyler</p>]]></content:encoded></item><item><title><![CDATA[AgentCore: MCP Gateway (2/4)🔥]]></title><description><![CDATA[aka, centralize those MCPs (and auth!)]]></description><link>https://www.letsdodevops.com/p/aws-agentcore-agentic-slack-bot-full-a98</link><guid isPermaLink="false">https://www.letsdodevops.com/p/aws-agentcore-agentic-slack-bot-full-a98</guid><dc:creator><![CDATA[Kyler Middleton]]></dc:creator><pubDate>Tue, 17 Feb 2026 15:30:15 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!S2dz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1039a1b-4bf4-4cc1-9bd9-661c76c36a89_1492x1342.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote><p><em>This blog series focuses on presenting complex DevOps projects as simple and approachable via plain language and lots of pictures. You can do it!</em></p><p>These articles are supported by readers, <a href="https://www.letsdodevops.com/subscribe">please consider subscribing</a> to support me writing more of these articles &lt;3 :)</p></blockquote><p>Hey all! </p><p>In the last article, we talked about why I migrated from Lambda to AWS Bedrock AgentCore, the architecture with two Lambdas feeding into an AgentCore runtime, and the features you get out of the box.</p><p>This article is part of a series: </p><ul><li><p>Part 1 - <a href="https://www.letsdodevops.com/p/aws-agentcore-agentic-slack-bot-full">AgentCore: Full Architecture and Code</a></p></li><li><p>Part 2 (this article) - <a href="https://www.letsdodevops.com/p/aws-agentcore-agentic-slack-bot-full-a98">AgentCore MCP Gateway</a></p></li><li><p>Part 3 - <a href="https://www.letsdodevops.com/p/aws-agentcore-agentic-slack-bot-agentcore">AgentCore Memory and Tools</a></p></li><li><p>Part 4 - <a href="https://www.letsdodevops.com/p/agentcore-deployment-operations-and">AgentCore Deployment, Operations, and Lessons Learned</a></p></li></ul><p>Now let&#8217;s talk about tools.</p><p>Vera (our Slack bot) currently has access to tools from PagerDuty, GitHub, Atlassian (Jira + Confluence), Azure, AWS CLI, and Splunk. That&#8217;s a lot of tool providers, and each one has its own authentication mechanism, transport protocol, and quirks. Managing all of that in application code can cause a lot of sprawl. </p><p>AgentCore&#8217;s MCP Gateway is designed to solve this - a single, managed endpoint for all your tool providers. But today, the gateway doesn&#8217;t support every auth pattern, so I&#8217;m running a hybrid: some tools go through the gateway, others connect directly. This article covers both patterns, how they coexist, and where this is all heading.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!S2dz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1039a1b-4bf4-4cc1-9bd9-661c76c36a89_1492x1342.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!S2dz!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1039a1b-4bf4-4cc1-9bd9-661c76c36a89_1492x1342.png 424w, https://substackcdn.com/image/fetch/$s_!S2dz!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1039a1b-4bf4-4cc1-9bd9-661c76c36a89_1492x1342.png 848w, https://substackcdn.com/image/fetch/$s_!S2dz!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1039a1b-4bf4-4cc1-9bd9-661c76c36a89_1492x1342.png 1272w, https://substackcdn.com/image/fetch/$s_!S2dz!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1039a1b-4bf4-4cc1-9bd9-661c76c36a89_1492x1342.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!S2dz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1039a1b-4bf4-4cc1-9bd9-661c76c36a89_1492x1342.png" width="1456" height="1310" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c1039a1b-4bf4-4cc1-9bd9-661c76c36a89_1492x1342.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1310,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:138690,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.letsdodevops.com/i/187235116?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1039a1b-4bf4-4cc1-9bd9-661c76c36a89_1492x1342.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!S2dz!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1039a1b-4bf4-4cc1-9bd9-661c76c36a89_1492x1342.png 424w, https://substackcdn.com/image/fetch/$s_!S2dz!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1039a1b-4bf4-4cc1-9bd9-661c76c36a89_1492x1342.png 848w, https://substackcdn.com/image/fetch/$s_!S2dz!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1039a1b-4bf4-4cc1-9bd9-661c76c36a89_1492x1342.png 1272w, https://substackcdn.com/image/fetch/$s_!S2dz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1039a1b-4bf4-4cc1-9bd9-661c76c36a89_1492x1342.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>If you want a deeper look at MCP tool integration with the Strands SDK specifically, I covered that in a previous article. We&#8217;ll be lighter on that here and focus more on the gateway pattern.</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;64b7a338-ada1-4719-b54b-2fc13435b1e2&quot;,&quot;caption&quot;:&quot;This blog series focuses on presenting complex DevOps projects as simple and approachable via plain language and lots of pictures. You can do it!&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;&#128293;Migrating VeraResearch to First-Class MCP Tool Support in Strands&#128293;&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:87384289,&quot;name&quot;:&quot;Kyler Middleton&quot;,&quot;bio&quot;:&quot;Kyler been a systems, network, call center, and security engineer, and is now a DevOps and software engineer. She speaks at any conference that will have her, hosts Day Two DevOps podcast, and writes up cool projects on Medium. Let's Do DevOps!&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f35f1a97-6536-4ac6-9a92-506396c09217_1410x1410.jpeg&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2025-12-16T15:30:30.361Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!CfzL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F526f9847-95e6-4c72-8c3c-504d12b0e172_1112x484.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://www.letsdodevops.com/p/migrating-veraresearch-to-first-class&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:180670655,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:2,&quot;comment_count&quot;:0,&quot;publication_id&quot;:2733228,&quot;publication_name&quot;:&quot;Let's Do DevOps&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!WsHj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbfbe0bf-9c66-4775-af63-3f2b58b03b80_1024x1024.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p>Let&#8217;s get into it.</p><h1>Local MCP Connections</h1><p>Before the gateway existed, every tool provider was a direct MCP connection managed in application code. That&#8217;s still how most of Vera&#8217;s tools work today, and it&#8217;s worth understanding the pattern before we talk about what the gateway changes.</p><p>MCP (Model Context Protocol) supports multiple transport types. Vera uses three of them:</p><h3>Stdio (local process)</h3><p>The MCP server runs as a subprocess inside the container. AWS CLI MCP works this way&#8212;it&#8217;s a Python process that Vera spawns and communicates with over stdin/stdout.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python"># AWS CLI: local process, stdio transport
aws_cli_mcp_client = MCPClient(
  lambda: stdio_client(
    StdioServerParameters(
      command=f&#8221;{opt_aws_cli_mcp_dir}/.venv/bin/awslabs.aws-api-mcp-server&#8221;,
      env=env,
    )
  ),
  prefix=&#8221;aws&#8221;,
)</code></pre></div><h3>Streamable HTTP (remote)</h3><p>The MCP server is hosted remotely and you connect over HTTP. GitHub and Splunk use this pattern, each with their own bearer token.</p><pre><code># GitHub: remote, streamable HTTP with bearer token
github_mcp_client = MCPClient(
lambda: streamablehttp_client(
&#8220;https://api.githubcopilot.com/mcp/&#8221;,
headers={&#8221;Authorization&#8221;: f&#8221;Bearer {github_token}&#8221;},
timeout=timedelta(seconds=30),
sse_read_timeout=timedelta(seconds=300),
    ),
tool_filters=tool_filters,
prefix=&#8221;github&#8221;,
)</code></pre><h3>SSE (remote, server-sent events)</h3><p>An older transport pattern. Atlassian&#8217;s MCP endpoint still uses this. It has its own token refresh dance&#8212;exchange a refresh token for an access token before connecting.</p><pre><code># Atlassian: remote, SSE with refresh token flow
access_token = get_access_token(refresh_token, client_id)
atlassian_mcp_client = MCPClient(
lambda: sse_client(
&#8220;https://mcp.atlassian.com/v1/sse&#8221;,
headers={&#8221;Authorization&#8221;: f&#8221;Bearer {access_token}&#8221;},
timeout=300.0,
    ),
tool_filters=tool_filters,
prefix=&#8221;atlassian&#8221;,
)</code></pre><p>This works. Each client connects independently, each manages its own auth, and they all feed tools into the same agent. But there are friction points:</p><p>Credential sprawl. Every provider needs its own secret stored, fetched, and refreshed. GitHub needs a PAT. Atlassian needs a client ID and refresh token. PagerDuty needs an API token. Splunk needs a bearer token. Azure needs a tenant ID, client ID, and client secret. That&#8217;s a lot of secrets to manage.</p><p>Per-provider auth code. Each provider has its own authentication pattern. Atlassian&#8217;s refresh token flow looks nothing like GitHub&#8217;s static PAT. Every new provider means writing another auth handler.</p><p>No centralized management. Want to see which tools your bot has access to? You need to read the code. Want to add a new provider? You need to write code, build a new container, and deploy. There&#8217;s no infrastructure-level view of &#8220;here are all the tools.&#8221;</p><p>The gateway changes this - for providers that support it.</p><h1>AgentCore MCP Gateway Pattern</h1><p>The AgentCore MCP Gateway is an AWS-managed service that sits between your agent and your tool providers. Instead of connecting directly to each provider, your agent connects to one gateway endpoint. The gateway handles routing, authentication, and tool discovery.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!a760!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F726f1697-3499-49a2-a986-07ab50fa90ad_1492x1342.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!a760!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F726f1697-3499-49a2-a986-07ab50fa90ad_1492x1342.png 424w, https://substackcdn.com/image/fetch/$s_!a760!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F726f1697-3499-49a2-a986-07ab50fa90ad_1492x1342.png 848w, https://substackcdn.com/image/fetch/$s_!a760!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F726f1697-3499-49a2-a986-07ab50fa90ad_1492x1342.png 1272w, https://substackcdn.com/image/fetch/$s_!a760!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F726f1697-3499-49a2-a986-07ab50fa90ad_1492x1342.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!a760!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F726f1697-3499-49a2-a986-07ab50fa90ad_1492x1342.png" width="1456" height="1310" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/726f1697-3499-49a2-a986-07ab50fa90ad_1492x1342.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1310,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:138690,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.letsdodevops.com/i/187235116?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F726f1697-3499-49a2-a986-07ab50fa90ad_1492x1342.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!a760!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F726f1697-3499-49a2-a986-07ab50fa90ad_1492x1342.png 424w, https://substackcdn.com/image/fetch/$s_!a760!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F726f1697-3499-49a2-a986-07ab50fa90ad_1492x1342.png 848w, https://substackcdn.com/image/fetch/$s_!a760!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F726f1697-3499-49a2-a986-07ab50fa90ad_1492x1342.png 1272w, https://substackcdn.com/image/fetch/$s_!a760!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F726f1697-3499-49a2-a986-07ab50fa90ad_1492x1342.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Tool registration moves from your application code to shared infrastructure. You define providers as gateway &#8220;targets&#8221; in Terraform. The gateway handles authentication to those providers using credentials stored in AgentCore&#8217;s Identity and token vault system. Your application code just connects to the gateway.</p><p>If authentication fails, the gateway can remove those tools without crashing your application. If you add tools, the gateway can provide those to any number of applications that use that gateway. This establishes a really scalable pattern for MCPs for your agents. </p><p>Here&#8217;s what that looks like in practice.</p><h1>Setting Up Cognito for Gateway Auth</h1><p>The gateway needs to authenticate your agent. It uses Cognito OAuth2 with the client credentials flow -machine-to-machine auth, no human login involved.</p><p>The setup has four pieces, all in Terraform:</p><p>1. A Cognito User Pool - the identity authority.</p><pre><code>resource &#8220;aws_cognito_user_pool&#8221; &#8220;gateway&#8221; {
  name = &#8220;${var.gateway_name}-${var.environment}&#8221;
  password_policy {
    minimum_length    = 16
    require_uppercase = true
    require_lowercase = true
    require_numbers   = true
    require_symbols   = true
  }
}</code></pre><p>2. Cognito Domain - provides the OAuth token endpoint.</p><pre><code>resource &#8220;aws_cognito_user_pool_domain&#8221; &#8220;gateway&#8221; {
  domain       = lower(&#8221;${var.gateway_name}-${var.environment}&#8221;)
  user_pool_id = aws_cognito_user_pool.gateway.id
}</code></pre><p>3. A Resource Server - defines the OAuth scope your agent needs.</p><pre><code>resource &#8220;aws_cognito_resource_server&#8221; &#8220;gateway&#8221; {
  identifier   = var.gateway_name
  name         = var.gateway_name
  user_pool_id = aws_cognito_user_pool.gateway.id
  scope {
    scope_name        = &#8220;invoke&#8221;
    scope_description = &#8220;Scope for invoking the ${var.gateway_name} AgentCore gateway&#8221;
  }
}</code></pre><p>4. An App Client - generates the credentials your agent uses.</p><pre><code>resource &#8220;aws_cognito_user_pool_client&#8221; &#8220;gateway&#8221; {
name         = &#8220;${var.gateway_name}-client-${var.environment}&#8221;
  user_pool_id = aws_cognito_user_pool.gateway.id
  # OAuth 2.0 client credentials flow (machine-to-machine)
  generate_secret                      = true
  allowed_oauth_flows_user_pool_client = true
  allowed_oauth_flows                  = [&#8221;client_credentials&#8221;]
  allowed_oauth_scopes = [
    &#8220;${aws_cognito_resource_server.gateway.identifier}/invoke&#8221;
  ]
  refresh_token_validity        = 30
  enable_token_revocation       = true
  supported_identity_providers  = [&#8221;COGNITO&#8221;]
}</code></pre><p>The Cognito client ID gets passed to the worker as an environment variable. The client secret goes into Secrets Manager and gets fetched at runtime. The token URL is constructed from the domain:</p><pre><code>https://{domain}.auth.{region}.amazoncognito.com/oauth2/token</code></pre><p>All of these outputs flow from the gateway module into the worker module:</p><pre><code># main.tf - wiring gateway outputs to worker
module &#8220;worker&#8221; {
  source = &#8220;./worker&#8221;
  # Gateway configurations
  gateway_arn       = module.gateway.gateway_arn
  gateway_client_id = module.gateway.cognito_client_id
  gateway_url       = module.gateway.gateway_url
  gateway_token_url = module.gateway.cognito_token_url
  gateway_scope     = module.gateway.gateway_scope
  # ... other config
}</code></pre><h1>The AgentCore Gateway Resource Itself</h1><p>With Cognito in place, the gateway resource is straightforward:</p><pre><code>resource &#8220;aws_bedrockagentcore_gateway&#8221; &#8220;main&#8221; {
  name        = var.gateway_name
  description = &#8220;Unified MCP gateway for ${var.runtime_name}&#8221;
  protocol_type = &#8220;MCP&#8221;
  protocol_configuration {
    mcp {
      supported_versions = [&#8221;2025-03-26&#8221;, &#8220;2025-06-18&#8221;, &#8220;2025-11-25&#8221;]
      search_type        = &#8220;SEMANTIC&#8221;
    }
  }
  # JWT authentication via Cognito
  authorizer_type = &#8220;CUSTOM_JWT&#8221;
  authorizer_configuration {
    custom_jwt_authorizer {
      discovery_url   = &#8220;https://cognito-idp.(region).amazonaws.com/${aws_cognito_user_pool.gateway.id}/.well-known/openid-configuration&#8221;
      allowed_clients = [aws_cognito_user_pool_client.gateway.id]
    }
  }
  role_arn        = aws_iam_role.gateway.arn
  exception_level = &#8220;DEBUG&#8221;
}</code></pre><p>There&#8217;s a few things to note. </p><h3>Semantic search</h3><p>The gateway supports `SEMANTIC` search type, which enables on-demand tool discovery based on what the user is asking rather than loading every tool upfront. In our current architecture, we load all gateway tools at startup since there are only a handful registered. But as more providers migrate to the gateway, on-demand semantic discovery will make more sense than loading hundreds of tools into the agent&#8217;s context on every invocation.</p><h3>MCP protocol versions</h3><p>The gateway supports multiple MCP versions. This is important because the protocol is evolving and different clients may use different versions.</p><h3>CUSTOM_JWT authorizer</h3><p>The gateway validates tokens by fetching the OIDC discovery document from Cognito. No custom validation code needed.</p><p>The gateway also needs IAM permissions -access to Secrets Manager for provider credentials, S3 for OpenAPI schemas, and the token vault for outbound authentication:</p><pre><code># Gateway needs access to provider credentials in token vault
resource &#8220;aws_iam_role_policy&#8221; &#8220;gateway_workload_identity&#8221; {
  name = &#8220;WorkloadIdentityAccess&#8221;
  role = aws_iam_role.gateway.id
  policy = jsonencode({
    Version = &#8220;2012-10-17&#8221;
    Statement = [
      {
        Effect = &#8220;Allow&#8221;
        Action = [&#8221;bedrock-agentcore:GetWorkloadAccessToken&#8221;]
        Resource = [
          &#8220;arn:aws:bedrock-agentcore:(region):(account ID):workload-identity-directory/default&#8221;,
          &#8220;arn:aws:bedrock-agentcore:(region):(account ID):workload-identity-directory/default/workload-identity/*&#8221;
        ]
      },
      {
        Effect = &#8220;Allow&#8221;
        Action = [&#8221;bedrock-agentcore:GetResourceApiKey&#8221;]
        Resource = [
          &#8220;arn:aws:bedrock-agentcore:(region):(account ID):workload-identity-directory/default&#8221;,
          &#8220;arn:aws:bedrock-agentcore:(region):(account ID):workload-identity-directory/default/workload-identity/*&#8221;,
          &#8220;arn:aws:bedrock-agentcore:(region):(account ID):token-vault/default&#8221;,
          &#8220;arn:aws:bedrock-agentcore:(region):(account ID):token-vault/default/apikeycredentialprovider/*&#8221;
        ]
      }
    ]
  })
}</code></pre><h1>PagerDuty on the Gateway</h1><h3>You thought this would be intuitive? lol</h3><p>Registering PagerDuty as a gateway target looks simple in Terraform:</p><pre><code>resource &#8220;aws_bedrockagentcore_gateway_target&#8221; &#8220;pagerduty&#8221; {
  gateway_identifier = aws_bedrockagentcore_gateway.main.gateway_id
  name               = &#8220;pagerduty&#8221;
  target_configuration {
    mcp {
      open_api_schema {
        s3 {
          uri = &#8220;s3://amazonbedrockagentcore-built-sampleschemas455e0815-oj7jujcd8xiu/pagerduty-open-api.json&#8221;
        }
      }
    }
  }
  credential_provider_configuration {
    api_key {
      provider_arn        = &#8220;arn:aws:bedrock-agentcore:(region):(account ID):token-vault/default/apikeycredentialprovider/pagerduty-vera-read-only&#8221;
      credential_prefix   = &#8220;Token&#8221;
      credential_location = &#8220;HEADER&#8221;
    }
  }
}</code></pre><blockquote><p>The URI I only got by building it by hand in the console and then importing it &#175;\_(&#12484;)_/&#175;</p></blockquote><p>Looks clean. But there are two things worth calling out.</p><p>First, the `provider_arn` references a credential provider in AgentCore&#8217;s token vault. That credential provider - an &#8220;outbound authentication&#8221; item in AgentCore Identity - has to be created separately. I did this manually through the AWS console rather than Terraform, because the token vault stores actual API keys and I didn&#8217;t want those in Terraform state. I always keep secrets separate from Terraform when I can.</p><p>The workflow is:</p><ol><li><p>Manually create an outbound authentication credential provider in AgentCore Identity</p></li><li><p>Store your PagerDuty API token in that credential provider</p></li><li><p>Reference the credential provider&#8217;s ARN in your Terraform gateway target configuration</p></li><li><p>Deploy the gateway and target with Terraform</p></li></ol><p>This separation makes sense from a security perspective - infrastructure definition stays in code, secrets stay out of it. You&#8217;re wiring together three things: the gateway target, a credential provider in the token vault, and the actual API key stored in that provider.</p><p>And then it didn&#8217;t work. Sweet. Turns out that even though this is an <strong>Integration</strong>, which I would think implies this is a vetted and tested pattern from AWS, it isn&#8217;t built correctly. Most APIs that auth with a bearer token use a header key of &#8220;bearer&#8221;, but PagerDuty doesn&#8217;t - it uses &#8220;Token&#8221;. </p><p>You can totally fix this in the Gateway Target (yay!) but I didn&#8217;t expect the special integration with PagerDuty to be broken out of the box (lol, boo). </p><p>Overrode the header value with credential_prefix = &#8220;Token&#8221; to fix it. Until we did, every PagerDuty API call failed with 401. </p><p>For an Integration that&#8217;s supposed to be turnkey, having to dig into PagerDuty&#8217;s auth docs to figure out their non-standard header format was a surprise.</p><h1>Token Caching Keeping the Gateway Fast</h1><p>Once the gateway is set up, your agent needs to authenticate to it on every request. That means fetching a JWT from Cognito. You don&#8217;t want to hit Cognito on every single tool call, so we cache tokens in memory.</p><pre><code># Token cache with expiry tracking
_token_cache = {&#8221;token&#8221;: None, &#8220;expires_at&#8221;: 0}
# Refresh 5 minutes before expiry to prevent race conditions
TOKEN_REFRESH_BUFFER = 300
def get_gateway_token(secrets_json):
    current_time = time.time()
# Check if token is expired or near expiry
if current_time &gt;= (_token_cache[&#8221;expires_at&#8221;] - TOKEN_REFRESH_BUFFER):
        client_id = os.environ.get(&#8221;GATEWAY_CLIENT_ID&#8221;)
        client_secret = secrets_json.get(&#8221;GATEWAY_CLIENT_SECRET&#8221;)
# Fetch new token from Cognito
        token_data = _fetch_new_token(client_id, client_secret)
# Update cache
        _token_cache[&#8221;token&#8221;] = token_data[&#8221;access_token&#8221;]
        _token_cache[&#8221;expires_at&#8221;] = current_time + int(
            token_data.get(&#8221;expires_in&#8221;, 3600)
        )
return _token_cache[&#8221;token&#8221;]</code></pre><p>The 5-minute buffer (`TOKEN_REFRESH_BUFFER = 300`) is important. Without it, you&#8217;d have a window where the token is technically valid when you check but expires before the request completes. Cognito tokens typically last an hour, so refreshing at 55 minutes is plenty safe.</p><p>This caching is especially effective in AgentCore because the container stays alive between requests. In Lambda, the cache only survived if you hit a warm container. In AgentCore, the cache persists for the lifetime of the runtime -up to 8 hours. One Cognito call can serve dozens of agent invocations.</p><p>The token gets injected into the transport via a factory function:</p><pre><code>def create_transport():
&#8220;&#8221;&#8220;Create streamable HTTP transport with fresh JWT token&#8221;&#8220;&#8221;
    token = get_gateway_token(secrets_json)
return streamablehttp_client(
        gateway_url,
headers={&#8221;Authorization&#8221;: f&#8221;Bearer {token}&#8221;},
timeout=timedelta(seconds=30),
sse_read_timeout=timedelta(seconds=300),
    )
gateway_mcp_client = MCPClient(
    create_transport,  # Factory called each time transport is needed
tool_filters=tool_filters,
)</code></pre><p>The factory pattern means each new transport connection gets a fresh (or cached) token automatically. No manual token management in the agent logic.</p><h1>Combine MCP Client Tools Together</h1><p>Today, Vera uses both patterns simultaneously. The agent initialization in `execute_agent()` builds a single tool list from all sources:</p><pre><code>def execute_agent(secrets_json, conversation, memory_config=None):
    tools = []
    opened_clients = {}
# Built-in tools (no MCP, bundled with Strands SDK)
from strands_tools import calculator, current_time, retrieve
    tools.extend([calculator, current_time, retrieve])
# Gateway MCP (PagerDuty today, more providers later)
try:
        gateway_mcp_client = build_gateway_mcp_client(secrets_json, mode=&#8221;read_only&#8221;)
        opened_clients[&#8221;Gateway&#8221;] = gateway_mcp_client
        tools.append(gateway_mcp_client)
except Exception as error:
print(f&#8221;Error setting up gateway MCP client: {str(error)}&#8221;)
# Direct MCP: GitHub
try:
        github_mcp_client = build_github_mcp_client(secrets_json[&#8221;GITHUB_TOKEN&#8221;], &#8220;read_only&#8221;)
        opened_clients[&#8221;GitHub&#8221;] = github_mcp_client
        tools.append(github_mcp_client)
except Exception as error:
print(f&#8221;Error setting up GitHub MCP client: {str(error)}&#8221;)
# All others MCPs also - Azure, AWS, etc.
# Build agent with ALL tools from ALL sources
    agent = Agent(
model=BedrockModel(model_id=model_id, ...),
system_prompt=system_prompt,
tools=tools,
    )</code></pre><p>The agent doesn&#8217;t know or care whether a tool came from the gateway or a direct connection. It sees a flat list of tools and uses whatever&#8217;s relevant to the user&#8217;s question. Ask about PagerDuty incidents? The gateway-routed PagerDuty tools handle it. Ask about a GitHub PR? The direct GitHub MCP client handles it.</p><p>The try/except means that if Atlassian&#8217;s MCP server is having a bad day, the agent still has PagerDuty, GitHub, AWS, and Splunk. Graceful degradation means you lose one provider, not all of them.</p><p>Each MCP target&#8217;s configuration is in its own file so we can do tool filtering (Vera is read-only) and makes it easy to sort out targets and authentication. </p><h1>The Future: When the Gateway Absorbs Everything</h1><p>Right now, the AgentCore MCP Gateway supports &#8220;Integrations&#8221; -providers that AWS has specifically built support for. PagerDuty is one of these. The gateway knows how to authenticate to PagerDuty, how to parse its OpenAPI schema, and how to route tool calls.</p><p>But GitHub, Atlassian, Splunk, and Azure aren&#8217;t Integrations yet. They use bearer token authentication over streamable HTTP or SSE, and the gateway&#8217;s credential provider system doesn&#8217;t support generic bearer token auth for arbitrary MCP endpoints today. That&#8217;s why these providers still need direct connections.</p><p>When AWS adds broader bearer token support to gateway targets -and the architecture is clearly heading that direction -the picture changes dramatically:</p><p>Today (hybrid):</p><pre><code>Agent &#8594; Gateway &#8594; PagerDuty (Integration)
Agent &#8594; Direct  &#8594; GitHub (bearer token, streamable HTTP)
Agent &#8594; Direct  &#8594; Atlassian (bearer token, SSE)
Agent &#8594; Direct  &#8594; Splunk (bearer token, streamable HTTP)
Agent &#8594; Direct  &#8594; AWS CLI (local stdio)</code></pre><p>Future (consolidated):</p><pre><code>Agent &#8594; Gateway &#8594; PagerDuty
Agent &#8594; Gateway &#8594; GitHub
Agent &#8594; Gateway &#8594; Atlassian (Jira + Confluence)
Agent &#8594; Gateway &#8594; Splunk
Agent &#8594; Direct  &#8594; AWS CLI (local stdio, will always be direct)</code></pre><p>When this migration is fully done, we&#8217;ll have: </p><ul><li><p>IAM-only auth - each agent runtime won&#8217;t have to manage MCP target authentication secrets at all</p></li><li><p>Infrastructure-level tool management. Adding a new provider means adding a Terraform resource - a new gateway target. No application code changes at all.</p></li><li><p>Centralized tool discovery. The gateway knows about all registered tools across all providers. With semantic search, the agent can find the right tool even if it doesn&#8217;t know the exact name.</p></li></ul><h1>What&#8217;s Next</h1><p>This article covered how Vera connects to its tool providers -the three MCP transport patterns, the AgentCore gateway as a managed alternative, the Cognito auth setup, and the PagerDuty configuration gotchas. We&#8217;re running a hybrid today: gateway where AWS supports it, direct connections everywhere else.</p><p>In the next article, we&#8217;ll cover AgentCore Memory - how Vera remembers things across conversations. Not just conversation history, but actual persistent knowledge: user preferences, learned facts, and session summaries. It&#8217;s one of the features that makes the AgentCore model feel genuinely different from Lambda.</p><p>The code for this entire project is open source: </p><blockquote><p><a href="https://github.com//KyMidd/AgentCore_AgenticSlackBot">GitHub/KyMidd/AgentCore_AgenticSlackBot</a></p></blockquote><p>Feel free to poke around, steal ideas, or open issues when things don&#8217;t make sense.</p><p>Happy building!<br>kyler</p>]]></content:encoded></item><item><title><![CDATA[AWS AgentCore Agentic Slack Bot - Full Architecture and Code (1/4)🔥]]></title><description><![CDATA[aka, go deploy this awesome thing please, it rules]]></description><link>https://www.letsdodevops.com/p/aws-agentcore-agentic-slack-bot-full</link><guid isPermaLink="false">https://www.letsdodevops.com/p/aws-agentcore-agentic-slack-bot-full</guid><dc:creator><![CDATA[Kyler Middleton]]></dc:creator><pubDate>Tue, 03 Feb 2026 16:46:19 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!7OeN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc30ca40e-0ac0-4089-8737-07e7f9c59a6f_1258x1058.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote><p><em>This blog series focuses on presenting complex DevOps projects as simple and approachable via plain language and lots of pictures. You can do it!</em></p><p>These articles are supported by readers, <a href="https://www.letsdodevops.com/subscribe">please consider subscribing</a> to support me writing more of these articles &lt;3 :)</p></blockquote><p>Hey all! </p><p>It&#8217;s been a while! I&#8217;ve been busy migrating Vera, my agentic AI helper, over to AWS AgentCore. </p><p>This article is part of a series: </p><ul><li><p>Part 1 (this article) - AgentCore: Full Architecture and Code</p></li><li><p>Part 2 - <a href="https://www.letsdodevops.com/p/aws-agentcore-agentic-slack-bot-full-a98">AgentCore MCP Gateway</a></p></li><li><p>Part 3 - <a href="https://www.letsdodevops.com/p/aws-agentcore-agentic-slack-bot-agentcore">AgentCore Memory and Tools</a></p></li><li><p>Part 4 - <a href="https://www.letsdodevops.com/p/agentcore-deployment-operations-and">AgentCore Deployment, Operations, and Lessons Learned</a></p></li></ul><p>AgentCore has a few very cool features that made it attractive for migration, including: </p><ul><li><p>Longer running jobs: Can run for 8 hours! Much longer than Lambda&#8217;s 15 minute hard limit</p></li><li><p>Avoiding cold starts: Lambda starts from scratch each time (with some exceptions for warm more expert-tier stuff), so having the bot ready to go is helpful</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!7OeN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc30ca40e-0ac0-4089-8737-07e7f9c59a6f_1258x1058.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!7OeN!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc30ca40e-0ac0-4089-8737-07e7f9c59a6f_1258x1058.png 424w, https://substackcdn.com/image/fetch/$s_!7OeN!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc30ca40e-0ac0-4089-8737-07e7f9c59a6f_1258x1058.png 848w, https://substackcdn.com/image/fetch/$s_!7OeN!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc30ca40e-0ac0-4089-8737-07e7f9c59a6f_1258x1058.png 1272w, https://substackcdn.com/image/fetch/$s_!7OeN!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc30ca40e-0ac0-4089-8737-07e7f9c59a6f_1258x1058.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!7OeN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc30ca40e-0ac0-4089-8737-07e7f9c59a6f_1258x1058.png" width="1258" height="1058" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c30ca40e-0ac0-4089-8737-07e7f9c59a6f_1258x1058.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1058,&quot;width&quot;:1258,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:91565,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.letsdodevops.com/i/186750859?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc30ca40e-0ac0-4089-8737-07e7f9c59a6f_1258x1058.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!7OeN!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc30ca40e-0ac0-4089-8737-07e7f9c59a6f_1258x1058.png 424w, https://substackcdn.com/image/fetch/$s_!7OeN!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc30ca40e-0ac0-4089-8737-07e7f9c59a6f_1258x1058.png 848w, https://substackcdn.com/image/fetch/$s_!7OeN!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc30ca40e-0ac0-4089-8737-07e7f9c59a6f_1258x1058.png 1272w, https://substackcdn.com/image/fetch/$s_!7OeN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc30ca40e-0ac0-4089-8737-07e7f9c59a6f_1258x1058.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>I&#8217;ve been building AI bots for enterprise use for a while now, and if you&#8217;ve been following along, you&#8217;ve seen the evolution. We started with a genAI Slack bot (https://www.letsdodevops.com/p/solving-aws-bedrocks-enterprise-logging) that could answer questions using RAG and knowledge bases. Then we leveled it up to an agentic Slack bot (https://www.letsdodevops.com/p/building-agentic-slack-bot) that could actually do things - query GitHub, check PagerDuty, search Jira.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!wd9g!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb82e8ff3-49fe-41d1-9b15-4ded6e06c17f_2700x1047.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!wd9g!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb82e8ff3-49fe-41d1-9b15-4ded6e06c17f_2700x1047.png 424w, https://substackcdn.com/image/fetch/$s_!wd9g!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb82e8ff3-49fe-41d1-9b15-4ded6e06c17f_2700x1047.png 848w, https://substackcdn.com/image/fetch/$s_!wd9g!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb82e8ff3-49fe-41d1-9b15-4ded6e06c17f_2700x1047.png 1272w, https://substackcdn.com/image/fetch/$s_!wd9g!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb82e8ff3-49fe-41d1-9b15-4ded6e06c17f_2700x1047.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!wd9g!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb82e8ff3-49fe-41d1-9b15-4ded6e06c17f_2700x1047.png" width="2700" height="1047" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b82e8ff3-49fe-41d1-9b15-4ded6e06c17f_2700x1047.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1047,&quot;width&quot;:2700,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:414506,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.letsdodevops.com/i/186750859?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3577729-523d-450d-bde1-9e0b1ded47d8_2700x1118.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!wd9g!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb82e8ff3-49fe-41d1-9b15-4ded6e06c17f_2700x1047.png 424w, https://substackcdn.com/image/fetch/$s_!wd9g!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb82e8ff3-49fe-41d1-9b15-4ded6e06c17f_2700x1047.png 848w, https://substackcdn.com/image/fetch/$s_!wd9g!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb82e8ff3-49fe-41d1-9b15-4ded6e06c17f_2700x1047.png 1272w, https://substackcdn.com/image/fetch/$s_!wd9g!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb82e8ff3-49fe-41d1-9b15-4ded6e06c17f_2700x1047.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Here&#8217;s the thing: that Lambda-based agentic bot? It works great. Seriously. Lambda with the Strands SDK running Claude can handle complex multi-tool workflows without breaking a sweat. If you&#8217;re building an agentic bot today and Lambda fits your needs, go for it.</p><p>But.</p><p>Lambda has a hard 15-minute timeout. For most agentic workflows, that&#8217;s plenty. But what happens when someone asks your bot to analyze a quarter&#8217;s worth of incidents, cross-reference them with deployment logs, and generate a report? Or when the agent needs to iterate through dozens of GitHub repos looking for a specific pattern? Those workflows can push past 15 minutes, and when Lambda hits that wall, it hits it hard.</p><p>Beyond the timeout, I kept hearing about AWS Bedrock AgentCore and its growing feature set. Memory that persists across conversations? A managed MCP gateway for tool access? Multi-agent orchestration? These aren&#8217;t things you can&#8217;t build on Lambda, but they&#8217;re things AgentCore gives you out of the box.</p><p>So I decided to migrate. Not because Lambda failed me, but because AgentCore offered a different model - one where my bot isn&#8217;t a function that spins up and dies, but an application that stays warm, maintains state, and processes requests like a real service.</p><p>This is the first article in a series documenting that migration. We&#8217;ll cover:</p><ol><li><p>This article: What AgentCore is and why the &#8220;living application&#8221; model matters</p></li><li><p>MCP Gateway - one gateway to rule all your tools</p></li><li><p>AgentCore Memory - teaching your bot to remember</p></li><li><p>Guardrails and Knowledge Bases - safety and smarts</p></li><li><p>Deployment, operations, and lessons learned</p></li></ol><p>Let&#8217;s do it. </p><p>If you want to skip all this architecture and just see the code, it&#8217;s all published here under an MIT license: </p><blockquote><p><a href="https://github.com/KyMidd/AgentCore_AgenticSlackBot">GitHub.com/KyMidd/AgentCore_AgenticSlackBot</a></p></blockquote><h1>The Problem with Lambda for AI Agents</h1><p>Don&#8217;t get me wrong - Lambda is fantastic for AI workloads. I&#8217;ve been running agentic bots on Lambda for months, and the model works well:</p><ul><li><p>Pay-per-invocation: You only pay when the bot is actually working</p></li><li><p>Auto-scaling: Slack blows up with requests? Lambda handles it</p></li><li><p>Simple deployment: Push code, done</p></li></ul><p>But there are friction points when you&#8217;re running long-lived agentic workflows:</p><p>The 15-minute ceiling. Most agent tasks complete in 2-5 minutes. But &#8220;most&#8221; isn&#8217;t &#8220;all.&#8221; When your agent needs to iterate through large datasets, make dozens of tool calls, or wait for slow external APIs, you can hit that timeout. And when you do, everything stops. No graceful degradation, no &#8220;let me finish this thought&#8221; - just death.</p><p>Cold starts during conversations. Lambda containers can be reused, but there&#8217;s no guarantee. If your user sends a follow-up message and it hits a cold container, you&#8217;re paying the initialization cost again - reloading models, re-establishing MCP connections, re-fetching secrets. It&#8217;s not catastrophic, but it&#8217;s not elegant either.</p><p>No persistent state. Every Lambda invocation starts fresh. Want to remember what you talked about 5 minutes ago? You need to build that yourself - fetch conversation history, reconstruct context, hope you got it right. Lambda doesn&#8217;t help you here.</p><p>The invoke-and-die pattern. This is the big mental model issue. Lambda wants you to do a thing and exit. Agents want to exist - to maintain connections, hold context, be ready for the next question. These models are fundamentally different.</p><p>None of these are dealbreakers. I worked around all of them. But when AgentCore offered a different approach, I was curious.</p><h1>Enter AgentCore: An Application, Not a Function</h1><p>AWS Bedrock AgentCore flips the model. Instead of invoking a function that dies after each request, you&#8217;re running a container that stays alive.</p><p>Think of it like this:</p><pre><code>| Aspect | Lambda | AgentCore |
|--------|--------|-----------|
| Lifecycle | Invoke &#8594; Execute &#8594; Die | Start &#8594; Stay warm &#8594; Process many requests &#8594; Eventually die |
| State | None (rebuild each time) | Maintained in memory |
| Timeout | 15 minutes hard limit | Configurable (hours) |
| Connections | Re-establish each invocation | Keep alive |
| Mental model | Function | Application |</code></pre><p>Your AgentCore runtime is a Docker container running on AWS-managed infrastructure. You define how long it can sit idle before AWS kills it (idle_timeout), and how long it can live total (max_lifetime). Between requests, it just... waits. Connections stay open. Memory stays loaded. When the next request comes in, you&#8217;re already warm.</p><p>This is a fundamental shift in how you think about your bot. You&#8217;re not writing a function anymore. You&#8217;re writing an application - one that handles HTTP health checks, processes incoming requests on a loop, and maintains state between calls.</p><p>The lambda pattern is to handle one single event and then die. The AgentCore pattern is your application spins up at the first request, and then it hangs around to handle other requests. </p><p>It&#8217;s fully warm, tokens are cached, MCP clients are prepped, and so we now need to understand which pieces of the application are shared vs distinct, and even which pieces of data are sensitive and need to be kept separate between requests. </p><p>The runtime stays warm based on your configuration:</p><pre><code># Runtime lifecycle configuration
idle_timeout = 900   # 15 minutes idle before shutdown
max_lifetime = 28800 # 8 hours max before forced restart</code></pre><p>AWS manages the health checks. If your container becomes unhealthy, AgentCore restarts it. If traffic spikes, AgentCore can scale. You focus on the application logic; AWS handles the infrastructure.</p><h1>Architecture Overview</h1><p>Here&#8217;s the thing about AgentCore: you can&#8217;t just point Slack directly at it. Slack requires a webhook response within 3 seconds, and your AI agent is definitely not responding in 3 seconds. So we still need Lambda - but in a different role.</p><p>The architecture looks like this:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!feio!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17c5a2ac-478d-4560-ab98-4ab801ffa89f_750x306.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!feio!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17c5a2ac-478d-4560-ab98-4ab801ffa89f_750x306.png 424w, https://substackcdn.com/image/fetch/$s_!feio!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17c5a2ac-478d-4560-ab98-4ab801ffa89f_750x306.png 848w, https://substackcdn.com/image/fetch/$s_!feio!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17c5a2ac-478d-4560-ab98-4ab801ffa89f_750x306.png 1272w, https://substackcdn.com/image/fetch/$s_!feio!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17c5a2ac-478d-4560-ab98-4ab801ffa89f_750x306.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!feio!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17c5a2ac-478d-4560-ab98-4ab801ffa89f_750x306.png" width="750" height="306" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/17c5a2ac-478d-4560-ab98-4ab801ffa89f_750x306.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:306,&quot;width&quot;:750,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:53529,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.letsdodevops.com/i/186750859?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17c5a2ac-478d-4560-ab98-4ab801ffa89f_750x306.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!feio!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17c5a2ac-478d-4560-ab98-4ab801ffa89f_750x306.png 424w, https://substackcdn.com/image/fetch/$s_!feio!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17c5a2ac-478d-4560-ab98-4ab801ffa89f_750x306.png 848w, https://substackcdn.com/image/fetch/$s_!feio!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17c5a2ac-478d-4560-ab98-4ab801ffa89f_750x306.png 1272w, https://substackcdn.com/image/fetch/$s_!feio!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17c5a2ac-478d-4560-ab98-4ab801ffa89f_750x306.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Receiver Lambda</h3><p>Catches the Slack webhook, validates the signature, and immediately returns 200. Slack is happy. Then it asynchronously invokes the Invoker Lambda. This function is tiny and fast - 128MB, sub-second execution.</p><h3>Invoker Lambda</h3><p>Takes the payload and calls InvokeAgentRuntime. This is a synchronous call to AgentCore, but the Invoker doesn&#8217;t wait for it to complete - it fires and forgets. The Invoker exists mainly to provide IAM isolation; it has only the permission to invoke AgentCore, nothing else.</p><h3>AgentCore Runtime</h3><p>The actual bot. Receives the request, loads conversation history, spins up MCP connections, runs the agent loop with Claude, and posts the response directly back to Slack. This is where the magic happens, and it can take as long as it needs.</p><p>Why the two-Lambda dance? Separation of concerns. The Receiver needs to be fast and needs access to validate Slack signatures. The Invoker needs permission to call AgentCore. By splitting them, we follow least-privilege principles and keep each component focused.</p><h1>What You Get Out of the Box</h1><p>AgentCore isn&#8217;t just &#8220;Lambda but longer.&#8221; It comes with a suite of features that would take significant effort to build yourself:</p><h3>MCP Gateway</h3><p>A managed gateway for MCP (Model Context Protocol) servers with OAuth2 authentication via Cognito. Register your tools once, authenticate once, access everything. We&#8217;ll dive deep into this in Article 2.</p><h3>AgentCore Memory</h3><p>Persistent memory across conversations. Not just &#8220;what did we talk about in this thread&#8221; but &#8220;what are this user&#8217;s preferences&#8221; and &#8220;what facts have we learned.&#8221; Three memory strategies out of the box: session summaries, user preferences, and semantic knowledge. Article 3 covers this in detail.</p><h3>Multi-Agent Orchestration</h3><p>This one&#8217;s interesting. AgentCore supports agent-to-agent communication and hierarchical agent patterns - think a &#8220;manager&#8221; agent that delegates to specialist agents. I haven&#8217;t implemented this yet, but the capability is there. A supervisor agent could coordinate between a &#8220;GitHub specialist&#8221; and a &#8220;Jira specialist,&#8221; each with their own tools and context. Something to explore in a future article.</p><h3>Container Lifecycle Management</h3><p>AWS handles health checks, restarts, and scaling. You define the parameters; AWS keeps things running.</p><h1>The Entrypoint Pattern: From Handler to Application</h1><p>We were already using Docker containers for the Lambda-based bot, so the Dockerfile doesn&#8217;t change dramatically. What changes is how the code runs inside that container.</p><p>With Lambda, you write a handler function that processes one event:</p><pre><code>def lambda_handler(event, context):
# Process this one request
# Return response
# Container may or may not stick around</code></pre><p>With AgentCore, you write an application that stays alive and processes many requests:</p><pre><code>from strands.multimodal.application import Application
app = Application()
@app.entrypoint
async def handle_slack_message(payload: dict):
# Process this request
# Container keeps running after we return
pass
if __name__ == &#8220;__main__&#8221;:
    app.run()  # Start HTTP server, wait for requests</code></pre><p>That app.run() call is the key difference. It starts an HTTP server inside the container that:</p><ul><li><p>Responds to health checks from AgentCore</p></li><li><p>Routes incoming requests to your handler</p></li><li><p>Keeps the process alive between requests</p></li></ul><p>Your handler gets called for each incoming request, but the container - and everything you&#8217;ve initialized - stays warm. MCP connections persist. Cached tokens stay valid. The mental model shifts from &#8220;spin up, do work, die&#8221; to &#8220;stay ready, handle work as it comes.&#8221;</p><h1>What&#8217;s Next</h1><p>This article covered the &#8220;why&#8221; and &#8220;what&#8221; of AgentCore. We talked about Lambda&#8217;s limitations for long-running agents, how AgentCore&#8217;s application model differs, and what features come bundled in.</p><p>In the next article, we&#8217;ll go deep on the MCP Gateway - how to set up unified authentication with Cognito, register multiple tool providers, and handle the quirks of different MCP servers (spoiler: Azure MCP on ARM64 is... special).</p><p>Until then, if you&#8217;re running agentic bots on Lambda and they&#8217;re working fine, don&#8217;t let this article convince you to migrate for migration&#8217;s sake. But if you&#8217;re hitting the 15-minute wall, or you want persistent memory, or you&#8217;re tired of rebuilding MCP clients on every invocation - AgentCore is worth a look.</p><p>The code for this entire project is open source: <a href="https://github.com/KyMidd/AgentCore_AgenticSlackBot">[GitHub link]</a>. Feel free to poke around, steal ideas, or open issues when things don&#8217;t make sense.</p><p>Happy building!<br>kyler</p>]]></content:encoded></item><item><title><![CDATA[🔥Migrating VeraResearch to First-Class MCP Tool Support in Strands🔥]]></title><description><![CDATA[aka, let them manage the complexity]]></description><link>https://www.letsdodevops.com/p/migrating-veraresearch-to-first-class</link><guid isPermaLink="false">https://www.letsdodevops.com/p/migrating-veraresearch-to-first-class</guid><dc:creator><![CDATA[Kyler Middleton]]></dc:creator><pubDate>Tue, 16 Dec 2025 15:30:30 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!CfzL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F526f9847-95e6-4c72-8c3c-504d12b0e172_1112x484.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote><p><em>This blog series focuses on presenting complex DevOps projects as simple and approachable via plain language and lots of pictures. You can do it!</em></p><p>These articles are supported by readers, <a href="https://www.letsdodevops.com/subscribe">please consider subscribing</a> to support me writing more of these articles &lt;3 :)</p></blockquote><p>Hey all!</p><p>Here&#8217;s the thing about building custom solutions: they work great until the library you depend on ships an update. Then you&#8217;re stuck choosing betIen &#8220;maintain my clever workaround&#8221; or &#8220;rewrite everything to match the new API.&#8221; Neither option sounds fun.</p><p>VeraResearch is an agentic Slack bot built with AWS Strands that connects to six different MCP (Model Context Protocol) servers&#8212;GitHub, Atlassian, PagerDuty, Azure, AWS CLI, and Splunk. Each MCP server provides tools the agent can use to ansIr questions and complete tasks. Pull incident data from PagerDuty. Search Jira tickets. List AWS resources. You get the idea.</p><p>When you load multiple MCP servers, they inevitably have overlapping tool names. GitHub has get_user. PagerDuty has get_user. The next MCP you install probably has get_user. Without prefixing, the agent crashes when it tries to load the second one. </p><p>So I built a solution: a custom PrefixedMCPTool wrapper class that manually prefixed every tool name. github_get_user, pagerduty_get_user, azure_get_user. Problem solved. It worked perfectly.</p><p>Except now I owned ~75 lines of code that reached into MCPClient internals, manually managed client lifecycles, extracted tool lists, wrapped them, and returned tuples. Every time Strands updated how MCPClient worked, I had to check if our custom wrapper still functioned. Maintenance debt.</p><p>Then <a href="https://github.com/strands-agents/sdk-python/pull/895">Strands PR #895</a> landed: first-class support for tool prefixing and filtering directly in MCPClient. The library now provides prefix and tool_filters parameters. Just pass a prefix string and optional tool filter instructions, and MCPClient handles everything.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!CfzL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F526f9847-95e6-4c72-8c3c-504d12b0e172_1112x484.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!CfzL!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F526f9847-95e6-4c72-8c3c-504d12b0e172_1112x484.png 424w, https://substackcdn.com/image/fetch/$s_!CfzL!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F526f9847-95e6-4c72-8c3c-504d12b0e172_1112x484.png 848w, https://substackcdn.com/image/fetch/$s_!CfzL!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F526f9847-95e6-4c72-8c3c-504d12b0e172_1112x484.png 1272w, https://substackcdn.com/image/fetch/$s_!CfzL!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F526f9847-95e6-4c72-8c3c-504d12b0e172_1112x484.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!CfzL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F526f9847-95e6-4c72-8c3c-504d12b0e172_1112x484.png" width="1112" height="484" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/526f9847-95e6-4c72-8c3c-504d12b0e172_1112x484.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:484,&quot;width&quot;:1112,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:91112,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.letsdodevops.com/i/180670655?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F526f9847-95e6-4c72-8c3c-504d12b0e172_1112x484.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!CfzL!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F526f9847-95e6-4c72-8c3c-504d12b0e172_1112x484.png 424w, https://substackcdn.com/image/fetch/$s_!CfzL!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F526f9847-95e6-4c72-8c3c-504d12b0e172_1112x484.png 848w, https://substackcdn.com/image/fetch/$s_!CfzL!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F526f9847-95e6-4c72-8c3c-504d12b0e172_1112x484.png 1272w, https://substackcdn.com/image/fetch/$s_!CfzL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F526f9847-95e6-4c72-8c3c-504d12b0e172_1112x484.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>If I&#8217;m maintaining custom abstractions, I&#8217;m on my own when breaking changes happen. If Strands provides the feature natively, their updates won&#8217;t break my code because they own the abstraction. </p><p>Let&#8217;s walk through removing my custom cruft and use the code the Strands maintainers team wrote.</p><blockquote><p>To skip right to the code changes, <a href="https://github.com/KyMidd/SlackStrandsAgenticBot/commit/e876f438c22f212113663353769d8a9dfc012938">see the commit here</a>. </p></blockquote><h1>The Old Way: Custom Tool Wrapping with PrefixedMCPTool</h1><p>The custom solution had three parts: a wrapper class, builder functions for each MCP server, and consumption code in worker_agent.py.</p><p>Here&#8217;s the PrefixedMCPTool wrapper class:</p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist143310091\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-prefixed_mcp_tool-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;prefixed_mcp_tool.py content, created by KyMidd on 03:16AM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;4\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;prefixed_mcp_tool.py\&quot;>\n        <tr>\n          <td id=\&quot;file-prefixed_mcp_tool-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-prefixed_mcp_tool-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>class</span> <span class=pl-v>PrefixedMCPTool</span>(<span class=pl-v>AgentTool</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-prefixed_mcp_tool-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-prefixed_mcp_tool-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s>&amp;quot;&amp;quot;&amp;quot;Wrapper that adds a prefix to an MCP tool&amp;#39;s name.&amp;quot;&amp;quot;&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-prefixed_mcp_tool-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-prefixed_mcp_tool-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>def</span> <span class=pl-en>__init__</span>(<span class=pl-s1>self</span>, <span class=pl-s1>tool</span>, <span class=pl-s1>prefix</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-prefixed_mcp_tool-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-prefixed_mcp_tool-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-en>super</span>().<span class=pl-c1>__init__</span>()</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-prefixed_mcp_tool-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-prefixed_mcp_tool-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>self</span>.<span class=pl-c1>_original_tool</span> <span class=pl-c1>=</span> <span class=pl-s1>tool</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-prefixed_mcp_tool-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-prefixed_mcp_tool-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>self</span>.<span class=pl-c1>_prefix</span> <span class=pl-c1>=</span> <span class=pl-s1>prefix</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-prefixed_mcp_tool-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-prefixed_mcp_tool-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>original_spec</span> <span class=pl-c1>=</span> <span class=pl-s1>tool</span>.<span class=pl-c1>tool_spec</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-prefixed_mcp_tool-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-prefixed_mcp_tool-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>self</span>.<span class=pl-c1>_prefixed_spec</span> <span class=pl-c1>=</span> <span class=pl-s1>original_spec</span>.<span class=pl-c1>copy</span>()</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-prefixed_mcp_tool-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-prefixed_mcp_tool-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>self</span>.<span class=pl-c1>_prefixed_spec</span>[<span class=pl-s>&amp;quot;name&amp;quot;</span>] <span class=pl-c1>=</span> <span class=pl-s>f&amp;quot;<span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>prefix</span><span class=pl-kos>}</span></span><span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>original_spec</span>[<span class=pl-s>&amp;#39;name&amp;#39;</span>]<span class=pl-kos>}</span></span>&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-prefixed_mcp_tool-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-prefixed_mcp_tool-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-en>@<span class=pl-s1>property</span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-prefixed_mcp_tool-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-prefixed_mcp_tool-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>def</span> <span class=pl-en>tool_spec</span>(<span class=pl-s1>self</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-prefixed_mcp_tool-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-prefixed_mcp_tool-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>return</span> <span class=pl-s1>self</span>.<span class=pl-c1>_prefixed_spec</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-prefixed_mcp_tool-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-prefixed_mcp_tool-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-en>@<span class=pl-s1>property</span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-prefixed_mcp_tool-py-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-prefixed_mcp_tool-py-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>def</span> <span class=pl-en>tool_name</span>(<span class=pl-s1>self</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-prefixed_mcp_tool-py-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-prefixed_mcp_tool-py-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>return</span> <span class=pl-s1>self</span>.<span class=pl-c1>_prefixed_spec</span>[<span class=pl-s>&amp;quot;name&amp;quot;</span>]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-prefixed_mcp_tool-py-L16\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;16\&quot;></td>\n          <td id=\&quot;file-prefixed_mcp_tool-py-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>def</span> <span class=pl-en>stream</span>(<span class=pl-s1>self</span>, <span class=pl-s1>tool_use</span>, <span class=pl-c1>*</span><span class=pl-s1>args</span>, <span class=pl-c1>**</span><span class=pl-s1>kwargs</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-prefixed_mcp_tool-py-L17\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;17\&quot;></td>\n          <td id=\&quot;file-prefixed_mcp_tool-py-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>return</span> <span class=pl-s1>self</span>.<span class=pl-c1>_original_tool</span>.<span class=pl-c1>stream</span>(<span class=pl-s1>tool_use</span>, <span class=pl-c1>*</span><span class=pl-s1>args</span>, <span class=pl-c1>**</span><span class=pl-s1>kwargs</span>)</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/81877fde60d95f25b1f6b6760b6de009/raw/972ff309516aeb19505840afd425437d0999cdbe/prefixed_mcp_tool.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/81877fde60d95f25b1f6b6760b6de009#file-prefixed_mcp_tool-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          prefixed_mcp_tool.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-0ac919313390.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-0ac919313390.css"><div id="gist143310091" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-prefixed_mcp_tool-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="4" data-paste-markdown-skip="" data-tagsearch-path="prefixed_mcp_tool.py">
        <tbody><tr>
          <td id="file-prefixed_mcp_tool-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-prefixed_mcp_tool-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">class</span> <span class="pl-v">PrefixedMCPTool</span>(<span class="pl-v">AgentTool</span>):</td>
        </tr>
        <tr>
          <td id="file-prefixed_mcp_tool-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-prefixed_mcp_tool-py-LC2" class="blob-code blob-code-inner js-file-line">    <span class="pl-s">"""Wrapper that adds a prefix to an MCP tool's name."""</span></td>
        </tr>
        <tr>
          <td id="file-prefixed_mcp_tool-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-prefixed_mcp_tool-py-LC3" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">def</span> <span class="pl-en">__init__</span>(<span class="pl-s1">self</span>, <span class="pl-s1">tool</span>, <span class="pl-s1">prefix</span>):</td>
        </tr>
        <tr>
          <td id="file-prefixed_mcp_tool-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-prefixed_mcp_tool-py-LC4" class="blob-code blob-code-inner js-file-line">        <span class="pl-en">super</span>().<span class="pl-c1">__init__</span>()</td>
        </tr>
        <tr>
          <td id="file-prefixed_mcp_tool-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-prefixed_mcp_tool-py-LC5" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">self</span>.<span class="pl-c1">_original_tool</span> <span class="pl-c1">=</span> <span class="pl-s1">tool</span></td>
        </tr>
        <tr>
          <td id="file-prefixed_mcp_tool-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-prefixed_mcp_tool-py-LC6" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">self</span>.<span class="pl-c1">_prefix</span> <span class="pl-c1">=</span> <span class="pl-s1">prefix</span></td>
        </tr>
        <tr>
          <td id="file-prefixed_mcp_tool-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-prefixed_mcp_tool-py-LC7" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">original_spec</span> <span class="pl-c1">=</span> <span class="pl-s1">tool</span>.<span class="pl-c1">tool_spec</span></td>
        </tr>
        <tr>
          <td id="file-prefixed_mcp_tool-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-prefixed_mcp_tool-py-LC8" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">self</span>.<span class="pl-c1">_prefixed_spec</span> <span class="pl-c1">=</span> <span class="pl-s1">original_spec</span>.<span class="pl-c1">copy</span>()</td>
        </tr>
        <tr>
          <td id="file-prefixed_mcp_tool-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-prefixed_mcp_tool-py-LC9" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">self</span>.<span class="pl-c1">_prefixed_spec</span>[<span class="pl-s">"name"</span>] <span class="pl-c1">=</span> <span class="pl-s">f"<span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">prefix</span><span class="pl-kos">}</span></span><span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">original_spec</span>[<span class="pl-s">'name'</span>]<span class="pl-kos">}</span></span>"</span></td>
        </tr>
        <tr>
          <td id="file-prefixed_mcp_tool-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-prefixed_mcp_tool-py-LC10" class="blob-code blob-code-inner js-file-line">    <span class="pl-en">@<span class="pl-s1">property</span></span></td>
        </tr>
        <tr>
          <td id="file-prefixed_mcp_tool-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-prefixed_mcp_tool-py-LC11" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">def</span> <span class="pl-en">tool_spec</span>(<span class="pl-s1">self</span>):</td>
        </tr>
        <tr>
          <td id="file-prefixed_mcp_tool-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-prefixed_mcp_tool-py-LC12" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">return</span> <span class="pl-s1">self</span>.<span class="pl-c1">_prefixed_spec</span></td>
        </tr>
        <tr>
          <td id="file-prefixed_mcp_tool-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-prefixed_mcp_tool-py-LC13" class="blob-code blob-code-inner js-file-line">    <span class="pl-en">@<span class="pl-s1">property</span></span></td>
        </tr>
        <tr>
          <td id="file-prefixed_mcp_tool-py-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-prefixed_mcp_tool-py-LC14" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">def</span> <span class="pl-en">tool_name</span>(<span class="pl-s1">self</span>):</td>
        </tr>
        <tr>
          <td id="file-prefixed_mcp_tool-py-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-prefixed_mcp_tool-py-LC15" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">return</span> <span class="pl-s1">self</span>.<span class="pl-c1">_prefixed_spec</span>[<span class="pl-s">"name"</span>]</td>
        </tr>
        <tr>
          <td id="file-prefixed_mcp_tool-py-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-prefixed_mcp_tool-py-LC16" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">def</span> <span class="pl-en">stream</span>(<span class="pl-s1">self</span>, <span class="pl-s1">tool_use</span>, <span class="pl-c1">*</span><span class="pl-s1">args</span>, <span class="pl-c1">**</span><span class="pl-s1">kwargs</span>):</td>
        </tr>
        <tr>
          <td id="file-prefixed_mcp_tool-py-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-prefixed_mcp_tool-py-LC17" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">return</span> <span class="pl-s1">self</span>.<span class="pl-c1">_original_tool</span>.<span class="pl-c1">stream</span>(<span class="pl-s1">tool_use</span>, <span class="pl-c1">*</span><span class="pl-s1">args</span>, <span class="pl-c1">**</span><span class="pl-s1">kwargs</span>)</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/81877fde60d95f25b1f6b6760b6de009/raw/972ff309516aeb19505840afd425437d0999cdbe/prefixed_mcp_tool.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/81877fde60d95f25b1f6b6760b6de009#file-prefixed_mcp_tool-py" class="Link--inTextBlock">
          prefixed_mcp_tool.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>Every MCP client builder followed the same pattern. Here&#8217;s GitHub:</p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist143310098\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-build_github_mcp_client_old-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;build_github_mcp_client_old.py content, created by KyMidd on 03:16AM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;4\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;build_github_mcp_client_old.py\&quot;>\n        <tr>\n          <td id=\&quot;file-build_github_mcp_client_old-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-build_github_mcp_client_old-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-en>build_github_mcp_client</span>(<span class=pl-s1>github_token</span>, <span class=pl-s1>mode</span><span class=pl-c1>=</span><span class=pl-s>&amp;quot;read_only&amp;quot;</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_github_mcp_client_old-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-build_github_mcp_client_old-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Build client</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_github_mcp_client_old-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-build_github_mcp_client_old-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>github_mcp_client</span> <span class=pl-c1>=</span> <span class=pl-en>MCPClient</span>(</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_github_mcp_client_old-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-build_github_mcp_client_old-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>lambda</span>: <span class=pl-en>streamablehttp_client</span>(</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_github_mcp_client_old-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-build_github_mcp_client_old-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s>&amp;quot;https://api.githubcopilot.com/mcp/&amp;quot;</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_github_mcp_client_old-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-build_github_mcp_client_old-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>headers</span><span class=pl-c1>=</span>{<span class=pl-s>&amp;quot;Authorization&amp;quot;</span>: <span class=pl-s>f&amp;quot;Bearer <span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>github_token</span><span class=pl-kos>}</span></span>&amp;quot;</span>},</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_github_mcp_client_old-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-build_github_mcp_client_old-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        )</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_github_mcp_client_old-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-build_github_mcp_client_old-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    )</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_github_mcp_client_old-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-build_github_mcp_client_old-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Manually start the client</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_github_mcp_client_old-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-build_github_mcp_client_old-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>github_client</span> <span class=pl-c1>=</span> <span class=pl-s1>github_mcp_client</span>.<span class=pl-c1>__enter__</span>()</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_github_mcp_client_old-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-build_github_mcp_client_old-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Extract all tools</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_github_mcp_client_old-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-build_github_mcp_client_old-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>all_github_tools</span> <span class=pl-c1>=</span> <span class=pl-s1>github_client</span>.<span class=pl-c1>list_tools_sync</span>()</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_github_mcp_client_old-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-build_github_mcp_client_old-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Filter for read-only tools</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_github_mcp_client_old-py-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-build_github_mcp_client_old-py-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>if</span> <span class=pl-s1>mode</span> <span class=pl-c1>==</span> <span class=pl-s>&amp;quot;read_only&amp;quot;</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_github_mcp_client_old-py-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-build_github_mcp_client_old-py-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>filtered_tools</span> <span class=pl-c1>=</span> []</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_github_mcp_client_old-py-L16\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;16\&quot;></td>\n          <td id=\&quot;file-build_github_mcp_client_old-py-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>for</span> <span class=pl-s1>tool</span> <span class=pl-c1>in</span> <span class=pl-s1>all_github_tools</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_github_mcp_client_old-py-L17\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;17\&quot;></td>\n          <td id=\&quot;file-build_github_mcp_client_old-py-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>tool_name</span> <span class=pl-c1>=</span> <span class=pl-s1>tool</span>.<span class=pl-c1>tool_spec</span>[<span class=pl-s>&amp;quot;name&amp;quot;</span>]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_github_mcp_client_old-py-L18\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;18\&quot;></td>\n          <td id=\&quot;file-build_github_mcp_client_old-py-LC18\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-k>if</span> <span class=pl-s1>tool_name</span>.<span class=pl-c1>startswith</span>((<span class=pl-s>&amp;quot;download_&amp;quot;</span>, <span class=pl-s>&amp;quot;get_&amp;quot;</span>, <span class=pl-s>&amp;quot;list_&amp;quot;</span>, <span class=pl-s>&amp;quot;search_&amp;quot;</span>)):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_github_mcp_client_old-py-L19\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;19\&quot;></td>\n          <td id=\&quot;file-build_github_mcp_client_old-py-LC19\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                <span class=pl-s1>filtered_tools</span>.<span class=pl-c1>append</span>(<span class=pl-s1>tool</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_github_mcp_client_old-py-L20\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;20\&quot;></td>\n          <td id=\&quot;file-build_github_mcp_client_old-py-LC20\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>return</span> <span class=pl-s1>github_mcp_client</span>, <span class=pl-s1>filtered_tools</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_github_mcp_client_old-py-L21\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;21\&quot;></td>\n          <td id=\&quot;file-build_github_mcp_client_old-py-LC21\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>return</span> <span class=pl-s1>github_mcp_client</span>, <span class=pl-s1>all_github_tools</span></td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/6c887403947d0e35942e81148859ee24/raw/7879871b2edfeecce10ed32cfa2f2c3367c2b3f8/build_github_mcp_client_old.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/6c887403947d0e35942e81148859ee24#file-build_github_mcp_client_old-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          build_github_mcp_client_old.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-0ac919313390.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-0ac919313390.css"><div id="gist143310098" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-build_github_mcp_client_old-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="4" data-paste-markdown-skip="" data-tagsearch-path="build_github_mcp_client_old.py">
        <tbody><tr>
          <td id="file-build_github_mcp_client_old-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-build_github_mcp_client_old-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-en">build_github_mcp_client</span>(<span class="pl-s1">github_token</span>, <span class="pl-s1">mode</span><span class="pl-c1">=</span><span class="pl-s">"read_only"</span>):</td>
        </tr>
        <tr>
          <td id="file-build_github_mcp_client_old-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-build_github_mcp_client_old-py-LC2" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Build client</span></td>
        </tr>
        <tr>
          <td id="file-build_github_mcp_client_old-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-build_github_mcp_client_old-py-LC3" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">github_mcp_client</span> <span class="pl-c1">=</span> <span class="pl-en">MCPClient</span>(</td>
        </tr>
        <tr>
          <td id="file-build_github_mcp_client_old-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-build_github_mcp_client_old-py-LC4" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">lambda</span>: <span class="pl-en">streamablehttp_client</span>(</td>
        </tr>
        <tr>
          <td id="file-build_github_mcp_client_old-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-build_github_mcp_client_old-py-LC5" class="blob-code blob-code-inner js-file-line">            <span class="pl-s">"https://api.githubcopilot.com/mcp/"</span>,</td>
        </tr>
        <tr>
          <td id="file-build_github_mcp_client_old-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-build_github_mcp_client_old-py-LC6" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">headers</span><span class="pl-c1">=</span>{<span class="pl-s">"Authorization"</span>: <span class="pl-s">f"Bearer <span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">github_token</span><span class="pl-kos">}</span></span>"</span>},</td>
        </tr>
        <tr>
          <td id="file-build_github_mcp_client_old-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-build_github_mcp_client_old-py-LC7" class="blob-code blob-code-inner js-file-line">        )</td>
        </tr>
        <tr>
          <td id="file-build_github_mcp_client_old-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-build_github_mcp_client_old-py-LC8" class="blob-code blob-code-inner js-file-line">    )</td>
        </tr>
        <tr>
          <td id="file-build_github_mcp_client_old-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-build_github_mcp_client_old-py-LC9" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Manually start the client</span></td>
        </tr>
        <tr>
          <td id="file-build_github_mcp_client_old-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-build_github_mcp_client_old-py-LC10" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">github_client</span> <span class="pl-c1">=</span> <span class="pl-s1">github_mcp_client</span>.<span class="pl-c1">__enter__</span>()</td>
        </tr>
        <tr>
          <td id="file-build_github_mcp_client_old-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-build_github_mcp_client_old-py-LC11" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Extract all tools</span></td>
        </tr>
        <tr>
          <td id="file-build_github_mcp_client_old-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-build_github_mcp_client_old-py-LC12" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">all_github_tools</span> <span class="pl-c1">=</span> <span class="pl-s1">github_client</span>.<span class="pl-c1">list_tools_sync</span>()</td>
        </tr>
        <tr>
          <td id="file-build_github_mcp_client_old-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-build_github_mcp_client_old-py-LC13" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Filter for read-only tools</span></td>
        </tr>
        <tr>
          <td id="file-build_github_mcp_client_old-py-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-build_github_mcp_client_old-py-LC14" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">if</span> <span class="pl-s1">mode</span> <span class="pl-c1">==</span> <span class="pl-s">"read_only"</span>:</td>
        </tr>
        <tr>
          <td id="file-build_github_mcp_client_old-py-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-build_github_mcp_client_old-py-LC15" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">filtered_tools</span> <span class="pl-c1">=</span> []</td>
        </tr>
        <tr>
          <td id="file-build_github_mcp_client_old-py-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-build_github_mcp_client_old-py-LC16" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">for</span> <span class="pl-s1">tool</span> <span class="pl-c1">in</span> <span class="pl-s1">all_github_tools</span>:</td>
        </tr>
        <tr>
          <td id="file-build_github_mcp_client_old-py-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-build_github_mcp_client_old-py-LC17" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">tool_name</span> <span class="pl-c1">=</span> <span class="pl-s1">tool</span>.<span class="pl-c1">tool_spec</span>[<span class="pl-s">"name"</span>]</td>
        </tr>
        <tr>
          <td id="file-build_github_mcp_client_old-py-L18" class="blob-num js-line-number js-blob-rnum" data-line-number="18"></td>
          <td id="file-build_github_mcp_client_old-py-LC18" class="blob-code blob-code-inner js-file-line">            <span class="pl-k">if</span> <span class="pl-s1">tool_name</span>.<span class="pl-c1">startswith</span>((<span class="pl-s">"download_"</span>, <span class="pl-s">"get_"</span>, <span class="pl-s">"list_"</span>, <span class="pl-s">"search_"</span>)):</td>
        </tr>
        <tr>
          <td id="file-build_github_mcp_client_old-py-L19" class="blob-num js-line-number js-blob-rnum" data-line-number="19"></td>
          <td id="file-build_github_mcp_client_old-py-LC19" class="blob-code blob-code-inner js-file-line">                <span class="pl-s1">filtered_tools</span>.<span class="pl-c1">append</span>(<span class="pl-s1">tool</span>)</td>
        </tr>
        <tr>
          <td id="file-build_github_mcp_client_old-py-L20" class="blob-num js-line-number js-blob-rnum" data-line-number="20"></td>
          <td id="file-build_github_mcp_client_old-py-LC20" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">return</span> <span class="pl-s1">github_mcp_client</span>, <span class="pl-s1">filtered_tools</span></td>
        </tr>
        <tr>
          <td id="file-build_github_mcp_client_old-py-L21" class="blob-num js-line-number js-blob-rnum" data-line-number="21"></td>
          <td id="file-build_github_mcp_client_old-py-LC21" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">return</span> <span class="pl-s1">github_mcp_client</span>, <span class="pl-s1">all_github_tools</span></td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/6c887403947d0e35942e81148859ee24/raw/7879871b2edfeecce10ed32cfa2f2c3367c2b3f8/build_github_mcp_client_old.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/6c887403947d0e35942e81148859ee24#file-build_github_mcp_client_old-py" class="Link--inTextBlock">
          build_github_mcp_client_old.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>Then in worker_agent.py, I consumed these tuples:</p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist143310105\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-worker_agent_old-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;worker_agent_old.py content, created by KyMidd on 03:17AM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;4\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;worker_agent_old.py\&quot;>\n        <tr>\n          <td id=\&quot;file-worker_agent_old-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-worker_agent_old-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c># Build GitHub MCP client with only read-only tools</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_agent_old-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-worker_agent_old-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s1>github_mcp_client</span>, <span class=pl-s1>github_tools</span> <span class=pl-c1>=</span> <span class=pl-en>build_github_mcp_client</span>(</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_agent_old-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-worker_agent_old-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>secrets_json</span>[<span class=pl-s>&amp;quot;GITHUB_TOKEN&amp;quot;</span>], <span class=pl-s>&amp;quot;read_only&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_agent_old-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-worker_agent_old-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_agent_old-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-worker_agent_old-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s1>opened_clients</span>[<span class=pl-s>&amp;quot;GitHub&amp;quot;</span>] <span class=pl-c1>=</span> <span class=pl-s1>github_mcp_client</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_agent_old-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-worker_agent_old-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c># Prefix tool names</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_agent_old-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-worker_agent_old-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s1>github_tools</span> <span class=pl-c1>=</span> <span class=pl-en>add_prefix_to_mcp_tools</span>(<span class=pl-s1>github_tools</span>, <span class=pl-s>&amp;quot;github&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_agent_old-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-worker_agent_old-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c># Extend tools list</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_agent_old-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-worker_agent_old-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s1>tools</span>.<span class=pl-c1>extend</span>(<span class=pl-s1>github_tools</span>)</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/02c0c6d04e5149238c58f529e71633b6/raw/bb7062d5a368a13c5525a79735b114d6d7894a2d/worker_agent_old.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/02c0c6d04e5149238c58f529e71633b6#file-worker_agent_old-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          worker_agent_old.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-0ac919313390.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-0ac919313390.css"><div id="gist143310105" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-worker_agent_old-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="4" data-paste-markdown-skip="" data-tagsearch-path="worker_agent_old.py">
        <tbody><tr>
          <td id="file-worker_agent_old-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-worker_agent_old-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-c"># Build GitHub MCP client with only read-only tools</span></td>
        </tr>
        <tr>
          <td id="file-worker_agent_old-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-worker_agent_old-py-LC2" class="blob-code blob-code-inner js-file-line"><span class="pl-s1">github_mcp_client</span>, <span class="pl-s1">github_tools</span> <span class="pl-c1">=</span> <span class="pl-en">build_github_mcp_client</span>(</td>
        </tr>
        <tr>
          <td id="file-worker_agent_old-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-worker_agent_old-py-LC3" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">secrets_json</span>[<span class="pl-s">"GITHUB_TOKEN"</span>], <span class="pl-s">"read_only"</span></td>
        </tr>
        <tr>
          <td id="file-worker_agent_old-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-worker_agent_old-py-LC4" class="blob-code blob-code-inner js-file-line">)</td>
        </tr>
        <tr>
          <td id="file-worker_agent_old-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-worker_agent_old-py-LC5" class="blob-code blob-code-inner js-file-line"><span class="pl-s1">opened_clients</span>[<span class="pl-s">"GitHub"</span>] <span class="pl-c1">=</span> <span class="pl-s1">github_mcp_client</span></td>
        </tr>
        <tr>
          <td id="file-worker_agent_old-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-worker_agent_old-py-LC6" class="blob-code blob-code-inner js-file-line"><span class="pl-c"># Prefix tool names</span></td>
        </tr>
        <tr>
          <td id="file-worker_agent_old-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-worker_agent_old-py-LC7" class="blob-code blob-code-inner js-file-line"><span class="pl-s1">github_tools</span> <span class="pl-c1">=</span> <span class="pl-en">add_prefix_to_mcp_tools</span>(<span class="pl-s1">github_tools</span>, <span class="pl-s">"github"</span>)</td>
        </tr>
        <tr>
          <td id="file-worker_agent_old-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-worker_agent_old-py-LC8" class="blob-code blob-code-inner js-file-line"><span class="pl-c"># Extend tools list</span></td>
        </tr>
        <tr>
          <td id="file-worker_agent_old-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-worker_agent_old-py-LC9" class="blob-code blob-code-inner js-file-line"><span class="pl-s1">tools</span>.<span class="pl-c1">extend</span>(<span class="pl-s1">github_tools</span>)</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/02c0c6d04e5149238c58f529e71633b6/raw/bb7062d5a368a13c5525a79735b114d6d7894a2d/worker_agent_old.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/02c0c6d04e5149238c58f529e71633b6#file-worker_agent_old-py" class="Link--inTextBlock">
          worker_agent_old.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>This pattern repeated six times for six MCP servers.</p><p>The problems: I&#8217;m calling <strong>__enter__</strong>() to manually start clients, but Agent expects unstarted clients and calls load_tools() itself. I&#8217;m reaching into MCPClient internals with list_tools_sync(). Every builder function returns a tuple that needs unpacking, prefixing, and extending. When Strands changes MCPClient behavior, this breaks.</p><h1>The New Way: What Strands Added and What I Deleted</h1><p>Strands PR #895 added three things to MCPClient: a prefix parameter, a tool_filters parameter, and ToolProvider interface support. Each addition let me delete chunks of custom code.</p><h2>Prefix Parameter</h2><p>Strands now adds a prefix to every tool name automatically. Pass prefix=&#8221;github&#8221; and the client handles the rest.</p><p>Before:</p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist143310343\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-before_manual_prefix-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;before_manual_prefix.py content, created by KyMidd on 03:20AM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;4\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;before_manual_prefix.py\&quot;>\n        <tr>\n          <td id=\&quot;file-before_manual_prefix-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-before_manual_prefix-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c># Wrap every tool manually</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-before_manual_prefix-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-before_manual_prefix-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s1>github_tools</span> <span class=pl-c1>=</span> <span class=pl-en>add_prefix_to_mcp_tools</span>(<span class=pl-s1>github_tools</span>, <span class=pl-s>&amp;quot;github&amp;quot;</span>)</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/df283ccec7cdcd1f276831eb70f327f3/raw/e426959edf6f673b4a37feebb44a86504ac73433/before_manual_prefix.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/df283ccec7cdcd1f276831eb70f327f3#file-before_manual_prefix-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          before_manual_prefix.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-0ac919313390.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-0ac919313390.css"><div id="gist143310343" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-before_manual_prefix-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="4" data-paste-markdown-skip="" data-tagsearch-path="before_manual_prefix.py">
        <tbody><tr>
          <td id="file-before_manual_prefix-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-before_manual_prefix-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-c"># Wrap every tool manually</span></td>
        </tr>
        <tr>
          <td id="file-before_manual_prefix-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-before_manual_prefix-py-LC2" class="blob-code blob-code-inner js-file-line"><span class="pl-s1">github_tools</span> <span class="pl-c1">=</span> <span class="pl-en">add_prefix_to_mcp_tools</span>(<span class="pl-s1">github_tools</span>, <span class="pl-s">"github"</span>)</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/df283ccec7cdcd1f276831eb70f327f3/raw/e426959edf6f673b4a37feebb44a86504ac73433/before_manual_prefix.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/df283ccec7cdcd1f276831eb70f327f3#file-before_manual_prefix-py" class="Link--inTextBlock">
          before_manual_prefix.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>After:</p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist143310344\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-after_mcp_prefix-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;after_mcp_prefix.py content, created by KyMidd on 03:20AM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;4\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;after_mcp_prefix.py\&quot;>\n        <tr>\n          <td id=\&quot;file-after_mcp_prefix-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-after_mcp_prefix-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c># MCPClient handles it</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-after_mcp_prefix-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-after_mcp_prefix-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s1>github_mcp_client</span> <span class=pl-c1>=</span> <span class=pl-en>MCPClient</span>(</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-after_mcp_prefix-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-after_mcp_prefix-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>lambda</span>: <span class=pl-en>streamablehttp_client</span>(...),</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-after_mcp_prefix-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-after_mcp_prefix-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>prefix</span><span class=pl-c1>=</span><span class=pl-s>&amp;quot;github&amp;quot;</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-after_mcp_prefix-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-after_mcp_prefix-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>)</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/c5cd3d3cb7d3c7aa66abcbfec95d1523/raw/644b6ba63c73a41ecf2b804a9fb9727949a4a7bb/after_mcp_prefix.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/c5cd3d3cb7d3c7aa66abcbfec95d1523#file-after_mcp_prefix-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          after_mcp_prefix.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-0ac919313390.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-0ac919313390.css"><div id="gist143310344" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-after_mcp_prefix-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="4" data-paste-markdown-skip="" data-tagsearch-path="after_mcp_prefix.py">
        <tbody><tr>
          <td id="file-after_mcp_prefix-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-after_mcp_prefix-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-c"># MCPClient handles it</span></td>
        </tr>
        <tr>
          <td id="file-after_mcp_prefix-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-after_mcp_prefix-py-LC2" class="blob-code blob-code-inner js-file-line"><span class="pl-s1">github_mcp_client</span> <span class="pl-c1">=</span> <span class="pl-en">MCPClient</span>(</td>
        </tr>
        <tr>
          <td id="file-after_mcp_prefix-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-after_mcp_prefix-py-LC3" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">lambda</span>: <span class="pl-en">streamablehttp_client</span>(...),</td>
        </tr>
        <tr>
          <td id="file-after_mcp_prefix-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-after_mcp_prefix-py-LC4" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">prefix</span><span class="pl-c1">=</span><span class="pl-s">"github"</span>,</td>
        </tr>
        <tr>
          <td id="file-after_mcp_prefix-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-after_mcp_prefix-py-LC5" class="blob-code blob-code-inner js-file-line">)</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/c5cd3d3cb7d3c7aa66abcbfec95d1523/raw/644b6ba63c73a41ecf2b804a9fb9727949a4a7bb/after_mcp_prefix.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/c5cd3d3cb7d3c7aa66abcbfec95d1523#file-after_mcp_prefix-py" class="Link--inTextBlock">
          after_mcp_prefix.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>I deleted the entire PrefixedMCPTool class (25 lines) and the add_prefix_to_mcp_tools() function.</p><h2>tool_filters Parameter</h2><p>Strands now filters tools using lambda functions you provide. Want only read-only tools? Pass a filter that checks tool names.</p><p>Before:</p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist143310349\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-before_manual_filter-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;before_manual_filter.py content, created by KyMidd on 03:20AM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;4\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;before_manual_filter.py\&quot;>\n        <tr>\n          <td id=\&quot;file-before_manual_filter-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-before_manual_filter-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c># Manually loop and filter</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-before_manual_filter-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-before_manual_filter-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s1>filtered_tools</span> <span class=pl-c1>=</span> []</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-before_manual_filter-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-before_manual_filter-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>for</span> <span class=pl-s1>tool</span> <span class=pl-c1>in</span> <span class=pl-s1>all_github_tools</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-before_manual_filter-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-before_manual_filter-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>tool_name</span> <span class=pl-c1>=</span> <span class=pl-s1>tool</span>.<span class=pl-c1>tool_spec</span>[<span class=pl-s>&amp;quot;name&amp;quot;</span>]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-before_manual_filter-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-before_manual_filter-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>if</span> <span class=pl-s1>tool_name</span>.<span class=pl-c1>startswith</span>((<span class=pl-s>&amp;quot;download_&amp;quot;</span>, <span class=pl-s>&amp;quot;get_&amp;quot;</span>, <span class=pl-s>&amp;quot;list_&amp;quot;</span>, <span class=pl-s>&amp;quot;search_&amp;quot;</span>)):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-before_manual_filter-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-before_manual_filter-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>filtered_tools</span>.<span class=pl-c1>append</span>(<span class=pl-s1>tool</span>)</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/926b47b66a70b0c120cb0a0c2f2cf4a2/raw/4609e262c0bebb3382cd63186ce944c61ebedc11/before_manual_filter.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/926b47b66a70b0c120cb0a0c2f2cf4a2#file-before_manual_filter-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          before_manual_filter.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-0ac919313390.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-0ac919313390.css"><div id="gist143310349" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-before_manual_filter-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="4" data-paste-markdown-skip="" data-tagsearch-path="before_manual_filter.py">
        <tbody><tr>
          <td id="file-before_manual_filter-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-before_manual_filter-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-c"># Manually loop and filter</span></td>
        </tr>
        <tr>
          <td id="file-before_manual_filter-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-before_manual_filter-py-LC2" class="blob-code blob-code-inner js-file-line"><span class="pl-s1">filtered_tools</span> <span class="pl-c1">=</span> []</td>
        </tr>
        <tr>
          <td id="file-before_manual_filter-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-before_manual_filter-py-LC3" class="blob-code blob-code-inner js-file-line"><span class="pl-k">for</span> <span class="pl-s1">tool</span> <span class="pl-c1">in</span> <span class="pl-s1">all_github_tools</span>:</td>
        </tr>
        <tr>
          <td id="file-before_manual_filter-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-before_manual_filter-py-LC4" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">tool_name</span> <span class="pl-c1">=</span> <span class="pl-s1">tool</span>.<span class="pl-c1">tool_spec</span>[<span class="pl-s">"name"</span>]</td>
        </tr>
        <tr>
          <td id="file-before_manual_filter-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-before_manual_filter-py-LC5" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">if</span> <span class="pl-s1">tool_name</span>.<span class="pl-c1">startswith</span>((<span class="pl-s">"download_"</span>, <span class="pl-s">"get_"</span>, <span class="pl-s">"list_"</span>, <span class="pl-s">"search_"</span>)):</td>
        </tr>
        <tr>
          <td id="file-before_manual_filter-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-before_manual_filter-py-LC6" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">filtered_tools</span>.<span class="pl-c1">append</span>(<span class="pl-s1">tool</span>)</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/926b47b66a70b0c120cb0a0c2f2cf4a2/raw/4609e262c0bebb3382cd63186ce944c61ebedc11/before_manual_filter.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/926b47b66a70b0c120cb0a0c2f2cf4a2#file-before_manual_filter-py" class="Link--inTextBlock">
          before_manual_filter.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>After:</p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist143310350\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-after_mcp_filters-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;after_mcp_filters.py content, created by KyMidd on 03:20AM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;4\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;after_mcp_filters.py\&quot;>\n        <tr>\n          <td id=\&quot;file-after_mcp_filters-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-after_mcp_filters-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c># MCPClient filters declaratively</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-after_mcp_filters-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-after_mcp_filters-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c1>TOOLS_PREFIX</span> <span class=pl-c1>=</span> <span class=pl-s>&amp;quot;github&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-after_mcp_filters-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-after_mcp_filters-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c1>READ_ONLY_PREFIXES</span> <span class=pl-c1>=</span> [<span class=pl-s>&amp;quot;download_&amp;quot;</span>, <span class=pl-s>&amp;quot;get_&amp;quot;</span>, <span class=pl-s>&amp;quot;list_&amp;quot;</span>, <span class=pl-s>&amp;quot;search_&amp;quot;</span>]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-after_mcp_filters-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-after_mcp_filters-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s1>github_mcp_client</span> <span class=pl-c1>=</span> <span class=pl-en>MCPClient</span>(</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-after_mcp_filters-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-after_mcp_filters-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>lambda</span>: <span class=pl-en>streamablehttp_client</span>(...),</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-after_mcp_filters-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-after_mcp_filters-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>tool_filters</span><span class=pl-c1>=</span>{</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-after_mcp_filters-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-after_mcp_filters-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s>&amp;quot;allowed&amp;quot;</span>: [<span class=pl-k>lambda</span> <span class=pl-s1>tool</span>: <span class=pl-s1>tool</span>.<span class=pl-c1>tool_name</span>.<span class=pl-c1>startswith</span>(<span class=pl-en>tuple</span>(</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-after_mcp_filters-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-after_mcp_filters-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s>f&amp;quot;<span class=pl-s1><span class=pl-kos>{</span><span class=pl-c1>TOOLS_PREFIX</span><span class=pl-kos>}</span></span>_<span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>p</span><span class=pl-kos>}</span></span>&amp;quot;</span> <span class=pl-k>for</span> <span class=pl-s1>p</span> <span class=pl-c1>in</span> <span class=pl-c1>READ_ONLY_PREFIXES</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-after_mcp_filters-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-after_mcp_filters-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        ))]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-after_mcp_filters-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-after_mcp_filters-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    },</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-after_mcp_filters-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-after_mcp_filters-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>prefix</span><span class=pl-c1>=</span><span class=pl-c1>TOOLS_PREFIX</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-after_mcp_filters-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-after_mcp_filters-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>)</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/fbb6263fa03a1db37cf2c891d647d12f/raw/eb9d73fe8ecb20a68cccf5efdb8d4ba342569cb7/after_mcp_filters.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/fbb6263fa03a1db37cf2c891d647d12f#file-after_mcp_filters-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          after_mcp_filters.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-0ac919313390.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-0ac919313390.css"><div id="gist143310350" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-after_mcp_filters-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="4" data-paste-markdown-skip="" data-tagsearch-path="after_mcp_filters.py">
        <tbody><tr>
          <td id="file-after_mcp_filters-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-after_mcp_filters-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-c"># MCPClient filters declaratively</span></td>
        </tr>
        <tr>
          <td id="file-after_mcp_filters-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-after_mcp_filters-py-LC2" class="blob-code blob-code-inner js-file-line"><span class="pl-c1">TOOLS_PREFIX</span> <span class="pl-c1">=</span> <span class="pl-s">"github"</span></td>
        </tr>
        <tr>
          <td id="file-after_mcp_filters-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-after_mcp_filters-py-LC3" class="blob-code blob-code-inner js-file-line"><span class="pl-c1">READ_ONLY_PREFIXES</span> <span class="pl-c1">=</span> [<span class="pl-s">"download_"</span>, <span class="pl-s">"get_"</span>, <span class="pl-s">"list_"</span>, <span class="pl-s">"search_"</span>]</td>
        </tr>
        <tr>
          <td id="file-after_mcp_filters-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-after_mcp_filters-py-LC4" class="blob-code blob-code-inner js-file-line"><span class="pl-s1">github_mcp_client</span> <span class="pl-c1">=</span> <span class="pl-en">MCPClient</span>(</td>
        </tr>
        <tr>
          <td id="file-after_mcp_filters-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-after_mcp_filters-py-LC5" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">lambda</span>: <span class="pl-en">streamablehttp_client</span>(...),</td>
        </tr>
        <tr>
          <td id="file-after_mcp_filters-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-after_mcp_filters-py-LC6" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">tool_filters</span><span class="pl-c1">=</span>{</td>
        </tr>
        <tr>
          <td id="file-after_mcp_filters-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-after_mcp_filters-py-LC7" class="blob-code blob-code-inner js-file-line">        <span class="pl-s">"allowed"</span>: [<span class="pl-k">lambda</span> <span class="pl-s1">tool</span>: <span class="pl-s1">tool</span>.<span class="pl-c1">tool_name</span>.<span class="pl-c1">startswith</span>(<span class="pl-en">tuple</span>(</td>
        </tr>
        <tr>
          <td id="file-after_mcp_filters-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-after_mcp_filters-py-LC8" class="blob-code blob-code-inner js-file-line">            <span class="pl-s">f"<span class="pl-s1"><span class="pl-kos">{</span><span class="pl-c1">TOOLS_PREFIX</span><span class="pl-kos">}</span></span>_<span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">p</span><span class="pl-kos">}</span></span>"</span> <span class="pl-k">for</span> <span class="pl-s1">p</span> <span class="pl-c1">in</span> <span class="pl-c1">READ_ONLY_PREFIXES</span></td>
        </tr>
        <tr>
          <td id="file-after_mcp_filters-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-after_mcp_filters-py-LC9" class="blob-code blob-code-inner js-file-line">        ))]</td>
        </tr>
        <tr>
          <td id="file-after_mcp_filters-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-after_mcp_filters-py-LC10" class="blob-code blob-code-inner js-file-line">    },</td>
        </tr>
        <tr>
          <td id="file-after_mcp_filters-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-after_mcp_filters-py-LC11" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">prefix</span><span class="pl-c1">=</span><span class="pl-c1">TOOLS_PREFIX</span>,</td>
        </tr>
        <tr>
          <td id="file-after_mcp_filters-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-after_mcp_filters-py-LC12" class="blob-code blob-code-inner js-file-line">)</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/fbb6263fa03a1db37cf2c891d647d12f/raw/eb9d73fe8ecb20a68cccf5efdb8d4ba342569cb7/after_mcp_filters.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/fbb6263fa03a1db37cf2c891d647d12f#file-after_mcp_filters-py" class="Link--inTextBlock">
          after_mcp_filters.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>I deleted the manual filtering loops from six MCP client files.</p><h2>ToolProvider Interface</h2><p>MCPClient now implements ToolProvider, which means Agent starts the client automatically when it needs tools. I don&#8217;t call <strong>__enter__</strong>() or list_tools_sync() anymore.</p><p>Before:</p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist143310351\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-before_manual_lifecycle-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;before_manual_lifecycle.py content, created by KyMidd on 03:20AM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;4\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;before_manual_lifecycle.py\&quot;>\n        <tr>\n          <td id=\&quot;file-before_manual_lifecycle-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-before_manual_lifecycle-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c># Build client</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-before_manual_lifecycle-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-before_manual_lifecycle-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s1>github_mcp_client</span> <span class=pl-c1>=</span> <span class=pl-en>MCPClient</span>(...)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-before_manual_lifecycle-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-before_manual_lifecycle-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c># Manually start it</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-before_manual_lifecycle-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-before_manual_lifecycle-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s1>github_client</span> <span class=pl-c1>=</span> <span class=pl-s1>github_mcp_client</span>.<span class=pl-c1>__enter__</span>()</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-before_manual_lifecycle-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-before_manual_lifecycle-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c># Extract tools</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-before_manual_lifecycle-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-before_manual_lifecycle-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s1>all_github_tools</span> <span class=pl-c1>=</span> <span class=pl-s1>github_client</span>.<span class=pl-c1>list_tools_sync</span>()</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-before_manual_lifecycle-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-before_manual_lifecycle-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c># Return tuple</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-before_manual_lifecycle-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-before_manual_lifecycle-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>return</span> <span class=pl-s1>github_mcp_client</span>, <span class=pl-s1>all_github_tools</span></td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/a75395e0c53290f18a61cdcb43afccc3/raw/4f94c39cc44fff73b29eda06162b86934efd30e0/before_manual_lifecycle.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/a75395e0c53290f18a61cdcb43afccc3#file-before_manual_lifecycle-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          before_manual_lifecycle.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-0ac919313390.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-0ac919313390.css"><div id="gist143310351" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-before_manual_lifecycle-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="4" data-paste-markdown-skip="" data-tagsearch-path="before_manual_lifecycle.py">
        <tbody><tr>
          <td id="file-before_manual_lifecycle-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-before_manual_lifecycle-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-c"># Build client</span></td>
        </tr>
        <tr>
          <td id="file-before_manual_lifecycle-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-before_manual_lifecycle-py-LC2" class="blob-code blob-code-inner js-file-line"><span class="pl-s1">github_mcp_client</span> <span class="pl-c1">=</span> <span class="pl-en">MCPClient</span>(...)</td>
        </tr>
        <tr>
          <td id="file-before_manual_lifecycle-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-before_manual_lifecycle-py-LC3" class="blob-code blob-code-inner js-file-line"><span class="pl-c"># Manually start it</span></td>
        </tr>
        <tr>
          <td id="file-before_manual_lifecycle-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-before_manual_lifecycle-py-LC4" class="blob-code blob-code-inner js-file-line"><span class="pl-s1">github_client</span> <span class="pl-c1">=</span> <span class="pl-s1">github_mcp_client</span>.<span class="pl-c1">__enter__</span>()</td>
        </tr>
        <tr>
          <td id="file-before_manual_lifecycle-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-before_manual_lifecycle-py-LC5" class="blob-code blob-code-inner js-file-line"><span class="pl-c"># Extract tools</span></td>
        </tr>
        <tr>
          <td id="file-before_manual_lifecycle-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-before_manual_lifecycle-py-LC6" class="blob-code blob-code-inner js-file-line"><span class="pl-s1">all_github_tools</span> <span class="pl-c1">=</span> <span class="pl-s1">github_client</span>.<span class="pl-c1">list_tools_sync</span>()</td>
        </tr>
        <tr>
          <td id="file-before_manual_lifecycle-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-before_manual_lifecycle-py-LC7" class="blob-code blob-code-inner js-file-line"><span class="pl-c"># Return tuple</span></td>
        </tr>
        <tr>
          <td id="file-before_manual_lifecycle-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-before_manual_lifecycle-py-LC8" class="blob-code blob-code-inner js-file-line"><span class="pl-k">return</span> <span class="pl-s1">github_mcp_client</span>, <span class="pl-s1">all_github_tools</span></td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/a75395e0c53290f18a61cdcb43afccc3/raw/4f94c39cc44fff73b29eda06162b86934efd30e0/before_manual_lifecycle.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/a75395e0c53290f18a61cdcb43afccc3#file-before_manual_lifecycle-py" class="Link--inTextBlock">
          before_manual_lifecycle.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>After:</p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist143310353\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-after_toolprovider-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;after_toolprovider.py content, created by KyMidd on 03:21AM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;4\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;after_toolprovider.py\&quot;>\n        <tr>\n          <td id=\&quot;file-after_toolprovider-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-after_toolprovider-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c># Build client</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-after_toolprovider-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-after_toolprovider-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s1>github_mcp_client</span> <span class=pl-c1>=</span> <span class=pl-en>MCPClient</span>(...)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-after_toolprovider-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-after_toolprovider-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c># Return it unstarted</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-after_toolprovider-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-after_toolprovider-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>return</span> <span class=pl-s1>github_mcp_client</span></td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/dacb532cb32618a84d284a4724a1a916/raw/2f150450f805d5dd25353a11a703ac7553ac42f5/after_toolprovider.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/dacb532cb32618a84d284a4724a1a916#file-after_toolprovider-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          after_toolprovider.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-0ac919313390.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-0ac919313390.css"><div id="gist143310353" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-after_toolprovider-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="4" data-paste-markdown-skip="" data-tagsearch-path="after_toolprovider.py">
        <tbody><tr>
          <td id="file-after_toolprovider-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-after_toolprovider-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-c"># Build client</span></td>
        </tr>
        <tr>
          <td id="file-after_toolprovider-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-after_toolprovider-py-LC2" class="blob-code blob-code-inner js-file-line"><span class="pl-s1">github_mcp_client</span> <span class="pl-c1">=</span> <span class="pl-en">MCPClient</span>(...)</td>
        </tr>
        <tr>
          <td id="file-after_toolprovider-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-after_toolprovider-py-LC3" class="blob-code blob-code-inner js-file-line"><span class="pl-c"># Return it unstarted</span></td>
        </tr>
        <tr>
          <td id="file-after_toolprovider-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-after_toolprovider-py-LC4" class="blob-code blob-code-inner js-file-line"><span class="pl-k">return</span> <span class="pl-s1">github_mcp_client</span></td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/dacb532cb32618a84d284a4724a1a916/raw/2f150450f805d5dd25353a11a703ac7553ac42f5/after_toolprovider.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/dacb532cb32618a84d284a4724a1a916#file-after_toolprovider-py" class="Link--inTextBlock">
          after_toolprovider.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>Then in worker_agent.py, instead of unpacking tuples and extending tool lists, I just append the client:</p><p>Before:</p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist143310356\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-before_worker_agent_tuples-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;before_worker_agent_tuples.py content, created by KyMidd on 03:21AM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;4\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;before_worker_agent_tuples.py\&quot;>\n        <tr>\n          <td id=\&quot;file-before_worker_agent_tuples-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-before_worker_agent_tuples-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s1>github_mcp_client</span>, <span class=pl-s1>github_tools</span> <span class=pl-c1>=</span> <span class=pl-en>build_github_mcp_client</span>(...)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-before_worker_agent_tuples-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-before_worker_agent_tuples-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s1>github_tools</span> <span class=pl-c1>=</span> <span class=pl-en>add_prefix_to_mcp_tools</span>(<span class=pl-s1>github_tools</span>, <span class=pl-s>&amp;quot;github&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-before_worker_agent_tuples-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-before_worker_agent_tuples-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s1>tools</span>.<span class=pl-c1>extend</span>(<span class=pl-s1>github_tools</span>)</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/9f549fa9a25e703dfe762161d956d1f9/raw/e4a10e9c0afdc3a61e2452d74170b5f31b5b0fe7/before_worker_agent_tuples.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/9f549fa9a25e703dfe762161d956d1f9#file-before_worker_agent_tuples-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          before_worker_agent_tuples.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-0ac919313390.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-0ac919313390.css"><div id="gist143310356" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-before_worker_agent_tuples-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="4" data-paste-markdown-skip="" data-tagsearch-path="before_worker_agent_tuples.py">
        <tbody><tr>
          <td id="file-before_worker_agent_tuples-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-before_worker_agent_tuples-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-s1">github_mcp_client</span>, <span class="pl-s1">github_tools</span> <span class="pl-c1">=</span> <span class="pl-en">build_github_mcp_client</span>(...)</td>
        </tr>
        <tr>
          <td id="file-before_worker_agent_tuples-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-before_worker_agent_tuples-py-LC2" class="blob-code blob-code-inner js-file-line"><span class="pl-s1">github_tools</span> <span class="pl-c1">=</span> <span class="pl-en">add_prefix_to_mcp_tools</span>(<span class="pl-s1">github_tools</span>, <span class="pl-s">"github"</span>)</td>
        </tr>
        <tr>
          <td id="file-before_worker_agent_tuples-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-before_worker_agent_tuples-py-LC3" class="blob-code blob-code-inner js-file-line"><span class="pl-s1">tools</span>.<span class="pl-c1">extend</span>(<span class="pl-s1">github_tools</span>)</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/9f549fa9a25e703dfe762161d956d1f9/raw/e4a10e9c0afdc3a61e2452d74170b5f31b5b0fe7/before_worker_agent_tuples.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/9f549fa9a25e703dfe762161d956d1f9#file-before_worker_agent_tuples-py" class="Link--inTextBlock">
          before_worker_agent_tuples.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>After:</p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist143310358\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-after_worker_agent_append-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;after_worker_agent_append.py content, created by KyMidd on 03:21AM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;4\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;after_worker_agent_append.py\&quot;>\n        <tr>\n          <td id=\&quot;file-after_worker_agent_append-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-after_worker_agent_append-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s1>github_mcp_client</span> <span class=pl-c1>=</span> <span class=pl-en>build_github_mcp_client</span>(...)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-after_worker_agent_append-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-after_worker_agent_append-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s1>tools</span>.<span class=pl-c1>append</span>(<span class=pl-s1>github_mcp_client</span>)</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/ce671d5d689f91be02339df97a5c9777/raw/a2e6f4b33ed856344a1f60b12d71d009dc1ad3f0/after_worker_agent_append.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/ce671d5d689f91be02339df97a5c9777#file-after_worker_agent_append-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          after_worker_agent_append.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-0ac919313390.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-0ac919313390.css"><div id="gist143310358" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-after_worker_agent_append-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="4" data-paste-markdown-skip="" data-tagsearch-path="after_worker_agent_append.py">
        <tbody><tr>
          <td id="file-after_worker_agent_append-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-after_worker_agent_append-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-s1">github_mcp_client</span> <span class="pl-c1">=</span> <span class="pl-en">build_github_mcp_client</span>(...)</td>
        </tr>
        <tr>
          <td id="file-after_worker_agent_append-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-after_worker_agent_append-py-LC2" class="blob-code blob-code-inner js-file-line"><span class="pl-s1">tools</span>.<span class="pl-c1">append</span>(<span class="pl-s1">github_mcp_client</span>)</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/ce671d5d689f91be02339df97a5c9777/raw/a2e6f4b33ed856344a1f60b12d71d009dc1ad3f0/after_worker_agent_append.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/ce671d5d689f91be02339df97a5c9777#file-after_worker_agent_append-py" class="Link--inTextBlock">
          after_worker_agent_append.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>Agent calls load_tools() on the client when it needs to, which starts the connection and extracts tools automatically.</p><h2><strong>Gotchas</strong></h2><ul><li><p><strong>Prefix separator</strong>: Strands adds an underscore between prefix and tool name automatically. I initially set TOOLS_PREFIX = &#8220;pagerduty_&#8221; (with trailing underscore) and got tool names like pagerduty__list_incidents (double underscore). Remove the trailing underscore from your prefix constants.</p></li><li><p><strong>Filter timing</strong>: tool_filters run after the prefix is applied. Check for github_get_user, not get_user.</p></li></ul><h1><strong>Summary</strong></h1><p>In this blog we walked through how to move from my custom-built MCP management interface to the MCP tool management pattern that the Strands maintainers built. It&#8217;s both less code for us to manage, as well as quite a bit faster to get all those MCPs built in parallel instead of in sequence. </p><p>Remember, every custom abstraction is maintenance debt when the upstream library changes. When the library ships the feature natively, future updates are forward compatible by default.</p><p>Always let someone else do the hard work. </p><blockquote><p>Full code here: https://github.com/KyMidd/SlackStrandsAgenticBot</p></blockquote><p>Good luck out there!<br>kyler</p>]]></content:encoded></item><item><title><![CDATA[🔥Building Audit Logging for Multi-Platform AI Bots with Python, AWS Cloudwatch🔥]]></title><description><![CDATA[aka, who is actually using our bots today?]]></description><link>https://www.letsdodevops.com/p/building-audit-logging-for-multi</link><guid isPermaLink="false">https://www.letsdodevops.com/p/building-audit-logging-for-multi</guid><dc:creator><![CDATA[Kyler Middleton]]></dc:creator><pubDate>Tue, 25 Nov 2025 15:30:51 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!nP8g!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b64a49c-1c99-43a4-a6c8-bc573f837f43_1456x637.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote><p><em>This blog series focuses on presenting complex DevOps projects as simple and approachable via plain language and lots of pictures. You can do it!</em></p><p>These articles are supported by readers, <a href="https://www.letsdodevops.com/subscribe">please consider subscribing</a> to support me writing more of these articles &lt;3 :)</p></blockquote><p>Hey all!</p><p>We&#8217;ve built three AI bots across our organization: VeraSlack, VeraTeams, and VeraResearch. All three run on AWS Bedrock using Claude Sonnet. They answer questions, search knowledge bases, and help employees find information.</p><p>When we first deployed these bots, we relied on AWS Bedrock&#8217;s built-in logging. Every API call generates a log entry with timestamps and request IDs. Our Lambda functions logged to CloudWatch. We figured we had full visibility. If security needed to audit a conversation, they&#8217;d just look at the logs.</p><p>The first time our security team asked &#8220;What did Bob from Finance ask the bot last Tuesday?&#8221; we couldn&#8217;t answer it. We had thousands of Bedrock API log entries, but no way to connect them to Bob&#8217;s question.</p><div class="pullquote"><p><strong>Agentic bots don&#8217;t make one API call per user question. They make dozens.</strong></p></div><p>An employee asks: &#8220;What&#8217;s our process for requesting SSL certificates?&#8221; That single question triggers the bot to query the knowledge base, call a reranking service, search Confluence, check PagerDuty, maybe query Jira, synthesize the results with Bedrock, and format a response. That&#8217;s fifteen separate API calls across multiple services.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!nP8g!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b64a49c-1c99-43a4-a6c8-bc573f837f43_1456x637.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!nP8g!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b64a49c-1c99-43a4-a6c8-bc573f837f43_1456x637.png 424w, https://substackcdn.com/image/fetch/$s_!nP8g!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b64a49c-1c99-43a4-a6c8-bc573f837f43_1456x637.png 848w, https://substackcdn.com/image/fetch/$s_!nP8g!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b64a49c-1c99-43a4-a6c8-bc573f837f43_1456x637.png 1272w, https://substackcdn.com/image/fetch/$s_!nP8g!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b64a49c-1c99-43a4-a6c8-bc573f837f43_1456x637.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!nP8g!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b64a49c-1c99-43a4-a6c8-bc573f837f43_1456x637.png" width="1456" height="637" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0b64a49c-1c99-43a4-a6c8-bc573f837f43_1456x637.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:637,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:321494,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.letsdodevops.com/i/178377544?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b64a49c-1c99-43a4-a6c8-bc573f837f43_1456x637.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!nP8g!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b64a49c-1c99-43a4-a6c8-bc573f837f43_1456x637.png 424w, https://substackcdn.com/image/fetch/$s_!nP8g!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b64a49c-1c99-43a4-a6c8-bc573f837f43_1456x637.png 848w, https://substackcdn.com/image/fetch/$s_!nP8g!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b64a49c-1c99-43a4-a6c8-bc573f837f43_1456x637.png 1272w, https://substackcdn.com/image/fetch/$s_!nP8g!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b64a49c-1c99-43a4-a6c8-bc573f837f43_1456x637.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Each API call logs separately. Each log entry is atomic&#8212;just a request ID, timestamp, and parameters. No user name. No original question. No conversation context.</p><p>To reconstruct what Bob asked, you&#8217;d need to find his username in Slack&#8217;s event logs, correlate that to a Lambda execution timestamp, connect it to fifteen different Bedrock API calls scattered across log groups, and piece together the conversation. </p><blockquote><p>Pro tip: This is awful, I don&#8217;t want to spend all my time reading logs</p></blockquote><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!zSZW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fafad5bab-2484-4df0-993a-e9655c9591c9_2474x334.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!zSZW!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fafad5bab-2484-4df0-993a-e9655c9591c9_2474x334.png 424w, https://substackcdn.com/image/fetch/$s_!zSZW!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fafad5bab-2484-4df0-993a-e9655c9591c9_2474x334.png 848w, https://substackcdn.com/image/fetch/$s_!zSZW!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fafad5bab-2484-4df0-993a-e9655c9591c9_2474x334.png 1272w, https://substackcdn.com/image/fetch/$s_!zSZW!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fafad5bab-2484-4df0-993a-e9655c9591c9_2474x334.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!zSZW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fafad5bab-2484-4df0-993a-e9655c9591c9_2474x334.png" width="1456" height="197" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/afad5bab-2484-4df0-993a-e9655c9591c9_2474x334.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:197,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:53653,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.letsdodevops.com/i/178377544?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fafad5bab-2484-4df0-993a-e9655c9591c9_2474x334.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!zSZW!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fafad5bab-2484-4df0-993a-e9655c9591c9_2474x334.png 424w, https://substackcdn.com/image/fetch/$s_!zSZW!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fafad5bab-2484-4df0-993a-e9655c9591c9_2474x334.png 848w, https://substackcdn.com/image/fetch/$s_!zSZW!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fafad5bab-2484-4df0-993a-e9655c9591c9_2474x334.png 1272w, https://substackcdn.com/image/fetch/$s_!zSZW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fafad5bab-2484-4df0-993a-e9655c9591c9_2474x334.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>The problem gets worse with conversational bots. VeraResearch uses Bedrock Agents and can have multi-turn conversations. A five-minute troubleshooting session generates hundreds of log entries. Bedrock logs API requests, but has no concept of &#8220;user,&#8221; &#8220;conversation,&#8221; or &#8220;session.&#8221;</p><p>We needed to answer basic questions: What is Bob asking? What information are we providing about sensitive topics? Which bot is being used most? Bedrock&#8217;s logging couldn&#8217;t answer these.</p><p>So we built our own audit logging system. One that captures user identity, the original question, conversation context, the bot&#8217;s response, and ties it together with a single session ID.</p><p>In this article, I&#8217;ll walk you through why platform logging fails for agentic bots, how we designed an audit system across Slack and Teams, and the platform-specific challenges we solved.</p><h1><strong>It&#8217;s Harder Than It Looks</strong></h1><p>Good audit logging needs specific fields: who asked (user_id), what they asked (user_query), the conversation context, what we told them (response), when it happened (timestamp), and which bot answered (bot_name). We also need the AWS account ID for environment tracking and a session ID that ties audit logs directly to Lambda execution logs.</p><p>We&#8217;re writing these logs to CloudWatch Logs using a shared log group called /aws/ai-bots/audit-logs. Each bot gets its own log stream pattern: VeraSlack/{session_id}, VeraTeams/{session_id}, VeraResearch/{session_id}. The session ID is the Lambda log stream name, which means we can correlate audit logs with execution logs instantly. No more hunting through multiple log groups trying to piece together what happened.</p><p>Here&#8217;s where it gets interesting: Slack and Teams are completely different platforms with different user identity models.</p><h2><strong>Slack is straightforward</strong> </h2><p>When a user messages the bot, we get a Slack event that includes their user ID. We already call the Slack API&#8217;s users.info endpoint to fetch the user&#8217;s profile for conversation building. That API returns a username like kyler.middleton. We just reuse that data for audit logging. No extra API calls, no complexity.</p><h2><strong>Teams is &#8230; different</strong></h2><blockquote><p>Honestly, I would have been surprised if Teams made this easy. Read through the whole Teams Vera series to learn all the awful patterns I had to build to make it work. This one isn&#8217;t as terrible as all that, but still not great. </p></blockquote><p>When a user messages the bot through Microsoft Teams, we get a Bot Framework event with an Azure AD Object ID&#8212;basically a GUID like d4bxxxx-xxxx-xxxx-xxxx-xxxxxxxxcbb. We also get a display name like &#8220;Kyler Middleton,&#8221; but that&#8217;s not useful for audit correlation. Display names aren&#8217;t unique and they change.</p><p>To get a consistent username from Teams, we need to call the Microsoft Graph API with that Azure AD Object ID. The Graph API returns the User Principal Name, which is typically an email address: Kyler.Middleton@veradigm.me. That&#8217;s better, but still not consistent with Slack&#8217;s format.</p><p>So we normalize it: split on the @ symbol, take everything before it, convert to lowercase. Kyler.Middleton@veradigm.me becomes kyler.middleton. Now it matches Slack&#8217;s username format, and we can search audit logs across platforms using consistent identifiers.</p><p>The normalization matters more than you&#8217;d think. When security asks &#8220;show me everything Bob asked across all bots,&#8221; they shouldn&#8217;t need to know that Bob is bob.smith in Slack but Bob.Smith@veradigm.me in Teams. One username format, searchable everywhere.</p><p>This also means we&#8217;re making an extra API call for every Teams interaction&#8212;but we do it after we&#8217;ve already responded to the user, so there&#8217;s no latency impact on the conversation.</p><h1><strong>Lets Build It!</strong></h1><p>The infrastructure setup is straightforward. We grant the Lambda IAM role write permissions to the shared CloudWatch log group `/aws/ai-bots/audit-logs`. All three bot Lambdas can write to this one log group, but they use different log stream prefixes to keep their logs separate.</p><p>The core audit logging function is identical across all three bots. It extracts the AWS account ID from the Lambda context, builds a structured log entry, and writes to CloudWatch:</p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist142571663\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-write_audit_log-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;write_audit_log.py content, created by KyMidd on 10:12PM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;4\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;write_audit_log.py\&quot;>\n        <tr>\n          <td id=\&quot;file-write_audit_log-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-write_audit_log-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-en>write_audit_log</span>(<span class=pl-s1>user_id</span>, <span class=pl-s1>session_id</span>, <span class=pl-s1>user_query</span>, <span class=pl-s1>full_conversation</span>, <span class=pl-s1>response</span>, <span class=pl-s1>conversation_id</span>, <span class=pl-s1>context</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-write_audit_log-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-write_audit_log-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>try</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-write_audit_log-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-write_audit_log-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>logs_client</span> <span class=pl-c1>=</span> <span class=pl-s1>boto3</span>.<span class=pl-c1>client</span>(<span class=pl-s>&amp;quot;logs&amp;quot;</span>, <span class=pl-s1>region_name</span><span class=pl-c1>=</span><span class=pl-s>&amp;quot;us-east-1&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-write_audit_log-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-write_audit_log-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>aws_account_id</span> <span class=pl-c1>=</span> <span class=pl-s1>context</span>.<span class=pl-c1>invoked_function_arn</span>.<span class=pl-c1>split</span>(<span class=pl-s>&amp;quot;:&amp;quot;</span>)[<span class=pl-c1>4</span>]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-write_audit_log-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-write_audit_log-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>log_entry</span> <span class=pl-c1>=</span> {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-write_audit_log-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-write_audit_log-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s>&amp;quot;timestamp&amp;quot;</span>: <span class=pl-s1>datetime</span>.<span class=pl-c1>now</span>(<span class=pl-s1>timezone</span>.<span class=pl-c1>utc</span>).<span class=pl-c1>isoformat</span>(),</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-write_audit_log-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-write_audit_log-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s>&amp;quot;bot_name&amp;quot;</span>: <span class=pl-s>&amp;quot;VeraResearch&amp;quot;</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-write_audit_log-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-write_audit_log-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s>&amp;quot;aws_account_id&amp;quot;</span>: <span class=pl-s1>aws_account_id</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-write_audit_log-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-write_audit_log-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s>&amp;quot;user_id&amp;quot;</span>: <span class=pl-s1>user_id</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-write_audit_log-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-write_audit_log-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s>&amp;quot;session_id&amp;quot;</span>: <span class=pl-s1>session_id</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-write_audit_log-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-write_audit_log-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s>&amp;quot;user_query&amp;quot;</span>: <span class=pl-s1>user_query</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-write_audit_log-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-write_audit_log-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s>&amp;quot;full_conversation&amp;quot;</span>: <span class=pl-s1>full_conversation</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-write_audit_log-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-write_audit_log-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s>&amp;quot;response&amp;quot;</span>: <span class=pl-s1>response</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-write_audit_log-py-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-write_audit_log-py-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s>&amp;quot;model_used&amp;quot;</span>: <span class=pl-s1>model_id</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-write_audit_log-py-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-write_audit_log-py-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s>&amp;quot;conversation_id&amp;quot;</span>: <span class=pl-s1>conversation_id</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-write_audit_log-py-L16\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;16\&quot;></td>\n          <td id=\&quot;file-write_audit_log-py-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        }</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-write_audit_log-py-L17\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;17\&quot;></td>\n          <td id=\&quot;file-write_audit_log-py-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>log_stream_name</span> <span class=pl-c1>=</span> <span class=pl-s>f&amp;quot;VeraResearch/<span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>session_id</span><span class=pl-kos>}</span></span>&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-write_audit_log-py-L18\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;18\&quot;></td>\n          <td id=\&quot;file-write_audit_log-py-LC18\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>try</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-write_audit_log-py-L19\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;19\&quot;></td>\n          <td id=\&quot;file-write_audit_log-py-LC19\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>logs_client</span>.<span class=pl-c1>create_log_stream</span>(<span class=pl-s1>logGroupName</span><span class=pl-c1>=</span><span class=pl-s1>audit_log_group_name</span>, <span class=pl-s1>logStreamName</span><span class=pl-c1>=</span><span class=pl-s1>log_stream_name</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-write_audit_log-py-L20\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;20\&quot;></td>\n          <td id=\&quot;file-write_audit_log-py-LC20\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>except</span> <span class=pl-s1>logs_client</span>.<span class=pl-c1>exceptions</span>.<span class=pl-c1>ResourceAlreadyExistsException</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-write_audit_log-py-L21\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;21\&quot;></td>\n          <td id=\&quot;file-write_audit_log-py-LC21\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-k>pass</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-write_audit_log-py-L22\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;22\&quot;></td>\n          <td id=\&quot;file-write_audit_log-py-LC22\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>logs_client</span>.<span class=pl-c1>put_log_events</span>(<span class=pl-s1>logGroupName</span><span class=pl-c1>=</span><span class=pl-s1>audit_log_group_name</span>, <span class=pl-s1>logStreamName</span><span class=pl-c1>=</span><span class=pl-s1>log_stream_name</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-write_audit_log-py-L23\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;23\&quot;></td>\n          <td id=\&quot;file-write_audit_log-py-LC23\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>logEvents</span><span class=pl-c1>=</span>[{<span class=pl-s>&amp;quot;timestamp&amp;quot;</span>: <span class=pl-en>int</span>(<span class=pl-s1>datetime</span>.<span class=pl-c1>now</span>(<span class=pl-s1>timezone</span>.<span class=pl-c1>utc</span>).<span class=pl-c1>timestamp</span>() <span class=pl-c1>*</span> <span class=pl-c1>1000</span>), <span class=pl-s>&amp;quot;message&amp;quot;</span>: <span class=pl-s1>json</span>.<span class=pl-c1>dumps</span>(<span class=pl-s1>log_entry</span>)}])</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-write_audit_log-py-L24\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;24\&quot;></td>\n          <td id=\&quot;file-write_audit_log-py-LC24\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>except</span> <span class=pl-v>Exception</span> <span class=pl-k>as</span> <span class=pl-s1>error</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-write_audit_log-py-L25\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;25\&quot;></td>\n          <td id=\&quot;file-write_audit_log-py-LC25\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-en>print</span>(<span class=pl-s>f&amp;quot;Error writing audit log: <span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>error</span><span class=pl-kos>}</span></span>&amp;quot;</span>)</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/56c490f0ea2a1bcba219d4511096d3b7/raw/2f9e5baf732d1cf5e58b9acbea17c87dc11b8c6b/write_audit_log.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/56c490f0ea2a1bcba219d4511096d3b7#file-write_audit_log-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          write_audit_log.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-0ac919313390.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-0ac919313390.css"><div id="gist142571663" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-write_audit_log-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="4" data-paste-markdown-skip="" data-tagsearch-path="write_audit_log.py">
        <tbody><tr>
          <td id="file-write_audit_log-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-write_audit_log-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-en">write_audit_log</span>(<span class="pl-s1">user_id</span>, <span class="pl-s1">session_id</span>, <span class="pl-s1">user_query</span>, <span class="pl-s1">full_conversation</span>, <span class="pl-s1">response</span>, <span class="pl-s1">conversation_id</span>, <span class="pl-s1">context</span>):</td>
        </tr>
        <tr>
          <td id="file-write_audit_log-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-write_audit_log-py-LC2" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">try</span>:</td>
        </tr>
        <tr>
          <td id="file-write_audit_log-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-write_audit_log-py-LC3" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">logs_client</span> <span class="pl-c1">=</span> <span class="pl-s1">boto3</span>.<span class="pl-c1">client</span>(<span class="pl-s">"logs"</span>, <span class="pl-s1">region_name</span><span class="pl-c1">=</span><span class="pl-s">"us-east-1"</span>)</td>
        </tr>
        <tr>
          <td id="file-write_audit_log-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-write_audit_log-py-LC4" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">aws_account_id</span> <span class="pl-c1">=</span> <span class="pl-s1">context</span>.<span class="pl-c1">invoked_function_arn</span>.<span class="pl-c1">split</span>(<span class="pl-s">":"</span>)[<span class="pl-c1">4</span>]</td>
        </tr>
        <tr>
          <td id="file-write_audit_log-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-write_audit_log-py-LC5" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">log_entry</span> <span class="pl-c1">=</span> {</td>
        </tr>
        <tr>
          <td id="file-write_audit_log-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-write_audit_log-py-LC6" class="blob-code blob-code-inner js-file-line">            <span class="pl-s">"timestamp"</span>: <span class="pl-s1">datetime</span>.<span class="pl-c1">now</span>(<span class="pl-s1">timezone</span>.<span class="pl-c1">utc</span>).<span class="pl-c1">isoformat</span>(),</td>
        </tr>
        <tr>
          <td id="file-write_audit_log-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-write_audit_log-py-LC7" class="blob-code blob-code-inner js-file-line">            <span class="pl-s">"bot_name"</span>: <span class="pl-s">"VeraResearch"</span>,</td>
        </tr>
        <tr>
          <td id="file-write_audit_log-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-write_audit_log-py-LC8" class="blob-code blob-code-inner js-file-line">            <span class="pl-s">"aws_account_id"</span>: <span class="pl-s1">aws_account_id</span>,</td>
        </tr>
        <tr>
          <td id="file-write_audit_log-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-write_audit_log-py-LC9" class="blob-code blob-code-inner js-file-line">            <span class="pl-s">"user_id"</span>: <span class="pl-s1">user_id</span>,</td>
        </tr>
        <tr>
          <td id="file-write_audit_log-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-write_audit_log-py-LC10" class="blob-code blob-code-inner js-file-line">            <span class="pl-s">"session_id"</span>: <span class="pl-s1">session_id</span>,</td>
        </tr>
        <tr>
          <td id="file-write_audit_log-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-write_audit_log-py-LC11" class="blob-code blob-code-inner js-file-line">            <span class="pl-s">"user_query"</span>: <span class="pl-s1">user_query</span>,</td>
        </tr>
        <tr>
          <td id="file-write_audit_log-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-write_audit_log-py-LC12" class="blob-code blob-code-inner js-file-line">            <span class="pl-s">"full_conversation"</span>: <span class="pl-s1">full_conversation</span>,</td>
        </tr>
        <tr>
          <td id="file-write_audit_log-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-write_audit_log-py-LC13" class="blob-code blob-code-inner js-file-line">            <span class="pl-s">"response"</span>: <span class="pl-s1">response</span>,</td>
        </tr>
        <tr>
          <td id="file-write_audit_log-py-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-write_audit_log-py-LC14" class="blob-code blob-code-inner js-file-line">            <span class="pl-s">"model_used"</span>: <span class="pl-s1">model_id</span>,</td>
        </tr>
        <tr>
          <td id="file-write_audit_log-py-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-write_audit_log-py-LC15" class="blob-code blob-code-inner js-file-line">            <span class="pl-s">"conversation_id"</span>: <span class="pl-s1">conversation_id</span>,</td>
        </tr>
        <tr>
          <td id="file-write_audit_log-py-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-write_audit_log-py-LC16" class="blob-code blob-code-inner js-file-line">        }</td>
        </tr>
        <tr>
          <td id="file-write_audit_log-py-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-write_audit_log-py-LC17" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">log_stream_name</span> <span class="pl-c1">=</span> <span class="pl-s">f"VeraResearch/<span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">session_id</span><span class="pl-kos">}</span></span>"</span></td>
        </tr>
        <tr>
          <td id="file-write_audit_log-py-L18" class="blob-num js-line-number js-blob-rnum" data-line-number="18"></td>
          <td id="file-write_audit_log-py-LC18" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">try</span>:</td>
        </tr>
        <tr>
          <td id="file-write_audit_log-py-L19" class="blob-num js-line-number js-blob-rnum" data-line-number="19"></td>
          <td id="file-write_audit_log-py-LC19" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">logs_client</span>.<span class="pl-c1">create_log_stream</span>(<span class="pl-s1">logGroupName</span><span class="pl-c1">=</span><span class="pl-s1">audit_log_group_name</span>, <span class="pl-s1">logStreamName</span><span class="pl-c1">=</span><span class="pl-s1">log_stream_name</span>)</td>
        </tr>
        <tr>
          <td id="file-write_audit_log-py-L20" class="blob-num js-line-number js-blob-rnum" data-line-number="20"></td>
          <td id="file-write_audit_log-py-LC20" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">except</span> <span class="pl-s1">logs_client</span>.<span class="pl-c1">exceptions</span>.<span class="pl-c1">ResourceAlreadyExistsException</span>:</td>
        </tr>
        <tr>
          <td id="file-write_audit_log-py-L21" class="blob-num js-line-number js-blob-rnum" data-line-number="21"></td>
          <td id="file-write_audit_log-py-LC21" class="blob-code blob-code-inner js-file-line">            <span class="pl-k">pass</span></td>
        </tr>
        <tr>
          <td id="file-write_audit_log-py-L22" class="blob-num js-line-number js-blob-rnum" data-line-number="22"></td>
          <td id="file-write_audit_log-py-LC22" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">logs_client</span>.<span class="pl-c1">put_log_events</span>(<span class="pl-s1">logGroupName</span><span class="pl-c1">=</span><span class="pl-s1">audit_log_group_name</span>, <span class="pl-s1">logStreamName</span><span class="pl-c1">=</span><span class="pl-s1">log_stream_name</span>,</td>
        </tr>
        <tr>
          <td id="file-write_audit_log-py-L23" class="blob-num js-line-number js-blob-rnum" data-line-number="23"></td>
          <td id="file-write_audit_log-py-LC23" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">logEvents</span><span class="pl-c1">=</span>[{<span class="pl-s">"timestamp"</span>: <span class="pl-en">int</span>(<span class="pl-s1">datetime</span>.<span class="pl-c1">now</span>(<span class="pl-s1">timezone</span>.<span class="pl-c1">utc</span>).<span class="pl-c1">timestamp</span>() <span class="pl-c1">*</span> <span class="pl-c1">1000</span>), <span class="pl-s">"message"</span>: <span class="pl-s1">json</span>.<span class="pl-c1">dumps</span>(<span class="pl-s1">log_entry</span>)}])</td>
        </tr>
        <tr>
          <td id="file-write_audit_log-py-L24" class="blob-num js-line-number js-blob-rnum" data-line-number="24"></td>
          <td id="file-write_audit_log-py-LC24" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">except</span> <span class="pl-v">Exception</span> <span class="pl-k">as</span> <span class="pl-s1">error</span>:</td>
        </tr>
        <tr>
          <td id="file-write_audit_log-py-L25" class="blob-num js-line-number js-blob-rnum" data-line-number="25"></td>
          <td id="file-write_audit_log-py-LC25" class="blob-code blob-code-inner js-file-line">        <span class="pl-en">print</span>(<span class="pl-s">f"Error writing audit log: <span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">error</span><span class="pl-kos">}</span></span>"</span>)</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/56c490f0ea2a1bcba219d4511096d3b7/raw/2f9e5baf732d1cf5e58b9acbea17c87dc11b8c6b/write_audit_log.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/56c490f0ea2a1bcba219d4511096d3b7#file-write_audit_log-py" class="Link--inTextBlock">
          write_audit_log.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>The function handles errors silently&#8212;if CloudWatch is having issues, we don&#8217;t want to fail the user&#8217;s request.</p><h2><strong>For Slack bots</strong></h2><p>We already have the user information from building the conversation. We extract the username and flatten the conversation history:</p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist142571675\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-slack-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;slack.py content, created by KyMidd on 10:12PM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;4\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;slack.py\&quot;>\n        <tr>\n          <td id=\&quot;file-slack-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-slack-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s1>user_id</span> <span class=pl-c1>=</span> <span class=pl-s1>user_info_json</span>.<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;user&amp;quot;</span>, {}).<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;name&amp;quot;</span>, <span class=pl-s>&amp;quot;unknown_user_id&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-slack-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-slack-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s1>session_id</span> <span class=pl-c1>=</span> <span class=pl-s1>context</span>.<span class=pl-c1>log_stream_name</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-slack-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-slack-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s1>full_conversation</span> <span class=pl-c1>=</span> <span class=pl-s>&amp;quot;<span class=pl-cce>\\n</span>&amp;quot;</span>.<span class=pl-c1>join</span>([</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-slack-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-slack-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>content</span>.<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;text&amp;quot;</span>, <span class=pl-s>&amp;quot;&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-slack-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-slack-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>for</span> <span class=pl-s1>item</span> <span class=pl-c1>in</span> <span class=pl-s1>conversation</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-slack-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-slack-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>for</span> <span class=pl-s1>content</span> <span class=pl-c1>in</span> <span class=pl-s1>item</span>.<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;content&amp;quot;</span>, [])</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-slack-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-slack-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>if</span> <span class=pl-en>isinstance</span>(<span class=pl-s1>content</span>, <span class=pl-s1>dict</span>) <span class=pl-c1>and</span> <span class=pl-s>&amp;quot;text&amp;quot;</span> <span class=pl-c1>in</span> <span class=pl-s1>content</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-slack-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-slack-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>])</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/ef7fa0bd4b2aa889df7281cc591dc12e/raw/57ee464cd2175835df02fa5527921d66aa02a9e8/slack.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/ef7fa0bd4b2aa889df7281cc591dc12e#file-slack-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          slack.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-0ac919313390.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-0ac919313390.css"><div id="gist142571675" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-slack-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="4" data-paste-markdown-skip="" data-tagsearch-path="slack.py">
        <tbody><tr>
          <td id="file-slack-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-slack-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-s1">user_id</span> <span class="pl-c1">=</span> <span class="pl-s1">user_info_json</span>.<span class="pl-c1">get</span>(<span class="pl-s">"user"</span>, {}).<span class="pl-c1">get</span>(<span class="pl-s">"name"</span>, <span class="pl-s">"unknown_user_id"</span>)</td>
        </tr>
        <tr>
          <td id="file-slack-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-slack-py-LC2" class="blob-code blob-code-inner js-file-line"><span class="pl-s1">session_id</span> <span class="pl-c1">=</span> <span class="pl-s1">context</span>.<span class="pl-c1">log_stream_name</span></td>
        </tr>
        <tr>
          <td id="file-slack-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-slack-py-LC3" class="blob-code blob-code-inner js-file-line"><span class="pl-s1">full_conversation</span> <span class="pl-c1">=</span> <span class="pl-s">"<span class="pl-cce">\n</span>"</span>.<span class="pl-c1">join</span>([</td>
        </tr>
        <tr>
          <td id="file-slack-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-slack-py-LC4" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">content</span>.<span class="pl-c1">get</span>(<span class="pl-s">"text"</span>, <span class="pl-s">""</span>)</td>
        </tr>
        <tr>
          <td id="file-slack-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-slack-py-LC5" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">for</span> <span class="pl-s1">item</span> <span class="pl-c1">in</span> <span class="pl-s1">conversation</span></td>
        </tr>
        <tr>
          <td id="file-slack-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-slack-py-LC6" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">for</span> <span class="pl-s1">content</span> <span class="pl-c1">in</span> <span class="pl-s1">item</span>.<span class="pl-c1">get</span>(<span class="pl-s">"content"</span>, [])</td>
        </tr>
        <tr>
          <td id="file-slack-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-slack-py-LC7" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">if</span> <span class="pl-en">isinstance</span>(<span class="pl-s1">content</span>, <span class="pl-s1">dict</span>) <span class="pl-c1">and</span> <span class="pl-s">"text"</span> <span class="pl-c1">in</span> <span class="pl-s1">content</span></td>
        </tr>
        <tr>
          <td id="file-slack-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-slack-py-LC8" class="blob-code blob-code-inner js-file-line">])</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/ef7fa0bd4b2aa889df7281cc591dc12e/raw/57ee464cd2175835df02fa5527921d66aa02a9e8/slack.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/ef7fa0bd4b2aa889df7281cc591dc12e#file-slack-py" class="Link--inTextBlock">
          slack.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><h2><strong>For Teams bots</strong></h2><p>We need the extra Graph API call to fetch the User Principal Name and normalize it:</p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist142571685\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-teams-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;teams.py content, created by KyMidd on 10:13PM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;4\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;teams.py\&quot;>\n        <tr>\n          <td id=\&quot;file-teams-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-teams-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-en>get_user_upn</span>(<span class=pl-s1>aad_object_id</span>, <span class=pl-s1>token</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-teams-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>response</span> <span class=pl-c1>=</span> <span class=pl-s1>requests</span>.<span class=pl-c1>get</span>(</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-teams-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s>f&amp;quot;https://graph.microsoft.com/v1.0/users/<span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>aad_object_id</span><span class=pl-kos>}</span></span>&amp;quot;</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-teams-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>headers</span><span class=pl-c1>=</span>{<span class=pl-s>&amp;quot;Authorization&amp;quot;</span>: <span class=pl-s>f&amp;quot;Bearer <span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>token</span><span class=pl-kos>}</span></span>&amp;quot;</span>},</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-teams-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>timeout</span><span class=pl-c1>=</span><span class=pl-c1>5</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-teams-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    )</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-teams-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>if</span> <span class=pl-s1>response</span>.<span class=pl-c1>ok</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-teams-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>user_data</span> <span class=pl-c1>=</span> <span class=pl-s1>response</span>.<span class=pl-c1>json</span>()</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-teams-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>upn</span> <span class=pl-c1>=</span> <span class=pl-s1>user_data</span>.<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;userPrincipalName&amp;quot;</span>) <span class=pl-c1>or</span> <span class=pl-s1>user_data</span>.<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;mail&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-teams-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>if</span> <span class=pl-s>&amp;quot;@&amp;quot;</span> <span class=pl-c1>in</span> <span class=pl-s1>upn</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-teams-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>upn</span> <span class=pl-c1>=</span> <span class=pl-s1>upn</span>.<span class=pl-c1>split</span>(<span class=pl-s>&amp;quot;@&amp;quot;</span>)[<span class=pl-c1>0</span>]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-teams-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>return</span> <span class=pl-s1>upn</span>.<span class=pl-c1>lower</span>()</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-teams-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>return</span> <span class=pl-s>&amp;quot;unknown_user_id&amp;quot;</span></td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/1aa7b80cb1e2eb69cc3dc3798615bd6b/raw/3a8f75ba6524540e629117574d0dd278ae9596ba/teams.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/1aa7b80cb1e2eb69cc3dc3798615bd6b#file-teams-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          teams.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-0ac919313390.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-0ac919313390.css"><div id="gist142571685" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-teams-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="4" data-paste-markdown-skip="" data-tagsearch-path="teams.py">
        <tbody><tr>
          <td id="file-teams-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-teams-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-en">get_user_upn</span>(<span class="pl-s1">aad_object_id</span>, <span class="pl-s1">token</span>):</td>
        </tr>
        <tr>
          <td id="file-teams-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-teams-py-LC2" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">response</span> <span class="pl-c1">=</span> <span class="pl-s1">requests</span>.<span class="pl-c1">get</span>(</td>
        </tr>
        <tr>
          <td id="file-teams-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-teams-py-LC3" class="blob-code blob-code-inner js-file-line">        <span class="pl-s">f"https://graph.microsoft.com/v1.0/users/<span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">aad_object_id</span><span class="pl-kos">}</span></span>"</span>,</td>
        </tr>
        <tr>
          <td id="file-teams-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-teams-py-LC4" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">headers</span><span class="pl-c1">=</span>{<span class="pl-s">"Authorization"</span>: <span class="pl-s">f"Bearer <span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">token</span><span class="pl-kos">}</span></span>"</span>},</td>
        </tr>
        <tr>
          <td id="file-teams-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-teams-py-LC5" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">timeout</span><span class="pl-c1">=</span><span class="pl-c1">5</span>,</td>
        </tr>
        <tr>
          <td id="file-teams-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-teams-py-LC6" class="blob-code blob-code-inner js-file-line">    )</td>
        </tr>
        <tr>
          <td id="file-teams-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-teams-py-LC7" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">if</span> <span class="pl-s1">response</span>.<span class="pl-c1">ok</span>:</td>
        </tr>
        <tr>
          <td id="file-teams-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-teams-py-LC8" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">user_data</span> <span class="pl-c1">=</span> <span class="pl-s1">response</span>.<span class="pl-c1">json</span>()</td>
        </tr>
        <tr>
          <td id="file-teams-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-teams-py-LC9" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">upn</span> <span class="pl-c1">=</span> <span class="pl-s1">user_data</span>.<span class="pl-c1">get</span>(<span class="pl-s">"userPrincipalName"</span>) <span class="pl-c1">or</span> <span class="pl-s1">user_data</span>.<span class="pl-c1">get</span>(<span class="pl-s">"mail"</span>)</td>
        </tr>
        <tr>
          <td id="file-teams-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-teams-py-LC10" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">if</span> <span class="pl-s">"@"</span> <span class="pl-c1">in</span> <span class="pl-s1">upn</span>:</td>
        </tr>
        <tr>
          <td id="file-teams-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-teams-py-LC11" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">upn</span> <span class="pl-c1">=</span> <span class="pl-s1">upn</span>.<span class="pl-c1">split</span>(<span class="pl-s">"@"</span>)[<span class="pl-c1">0</span>]</td>
        </tr>
        <tr>
          <td id="file-teams-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-teams-py-LC12" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">return</span> <span class="pl-s1">upn</span>.<span class="pl-c1">lower</span>()</td>
        </tr>
        <tr>
          <td id="file-teams-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-teams-py-LC13" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">return</span> <span class="pl-s">"unknown_user_id"</span></td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/1aa7b80cb1e2eb69cc3dc3798615bd6b/raw/3a8f75ba6524540e629117574d0dd278ae9596ba/teams.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/1aa7b80cb1e2eb69cc3dc3798615bd6b#file-teams-py" class="Link--inTextBlock">
          teams.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><h2>But doesn&#8217;t that slow the response down? </h2><p>To avoid introducing latency in the response, we log after responding to the user. The bot sends its response first, then writes the audit log. This keeps the user experience fast. If the Graph API is slow or CloudWatch has issues, the user never sees it.</p><h1>Lessons Learned</h1><p>The biggest lesson I took from this is that the platform&#8217;s (read: AWS Bedrock) logs were good enough for all our purposes. Even with great search tools like Splunk or Elastic/ELK, if the data isn&#8217;t in your logs, you can&#8217;t get to it. </p><p>So we had to build it ourselves. </p><p>Username normalization matters more than you&#8217;d expect. We could have just logged whatever each platform gave us, but then searching across platforms becomes painful. </p><p>Session correlation means it&#8217;s easier to dig in if there is more detail needed. The audit log contains the full cloudtrail log group name for us to click in and read through. </p><p>Log after the user response is posted so we introduce literally no latency in the user experience. </p><p>Audit error logging should be invisible to the user - just error log for admins. </p><p>AI compliance isn&#8217;t just about model outputs. It&#8217;s about understanding who&#8217;s asking what, what information we&#8217;re providing, and being able to trace any conversation from question to answer. </p><h1>Summary</h1><p>In this article we talked about how Bedrock doesn&#8217;t suite our needs for audit logging, and what we&#8217;d really like instead - one cloudtrail group, shipped to Splunk or ELK, that contains all the data we need (and only the data we need) in one place. </p><p>We linked each summary / audit log to the deep log group trail we need if we want to dig further</p><p>And we shared all the code to show you how to do it!! </p><p>Good luck out there. <br>kyler</p>]]></content:encoded></item><item><title><![CDATA[🔥Teaching Agentic Strands Agents to assume Cross-Account IAM Roles and Read AWS Resources🔥]]></title><description><![CDATA[aka, which VMs are running hot on CPU? Or out of disk space?]]></description><link>https://www.letsdodevops.com/p/teaching-agentic-strands-agents-to</link><guid isPermaLink="false">https://www.letsdodevops.com/p/teaching-agentic-strands-agents-to</guid><dc:creator><![CDATA[Kyler Middleton]]></dc:creator><pubDate>Wed, 12 Nov 2025 16:12:26 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!vj1D!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd162a756-6968-4530-b252-4eca9c58fe94_1024x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote><p><em>This blog series focuses on presenting complex DevOps projects as simple and approachable via plain language and lots of pictures. You can do it!</em></p><p>These articles are supported by readers, <a href="https://www.letsdodevops.com/subscribe">please consider subscribing</a> to support me writing more of these articles &lt;3 :)</p></blockquote><p>Hey all! </p><p>In this blog post, we&#8217;re going to integrate the <a href="https://github.com/awslabs/mcp/tree/main/src/aws-api-mcp-server">AWS Labs CLI MCP</a> server with a Strands-powered AI agent running in Lambda, with support for querying resources across multiple AWS accounts using profile-based role assumption.</p><p>The specific challenge: we need our AI assistant to query AWS resources across many different accounts (dev, staging, prod, ops, etc.)&#8230; with an MCP server that appears built to support a single authentication profile, in a single account. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!vj1D!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd162a756-6968-4530-b252-4eca9c58fe94_1024x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!vj1D!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd162a756-6968-4530-b252-4eca9c58fe94_1024x1024.png 424w, https://substackcdn.com/image/fetch/$s_!vj1D!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd162a756-6968-4530-b252-4eca9c58fe94_1024x1024.png 848w, https://substackcdn.com/image/fetch/$s_!vj1D!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd162a756-6968-4530-b252-4eca9c58fe94_1024x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!vj1D!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd162a756-6968-4530-b252-4eca9c58fe94_1024x1024.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!vj1D!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd162a756-6968-4530-b252-4eca9c58fe94_1024x1024.png" width="1024" height="1024" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d162a756-6968-4530-b252-4eca9c58fe94_1024x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1024,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:767983,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.letsdodevops.com/i/177063615?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd162a756-6968-4530-b252-4eca9c58fe94_1024x1024.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!vj1D!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd162a756-6968-4530-b252-4eca9c58fe94_1024x1024.png 424w, https://substackcdn.com/image/fetch/$s_!vj1D!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd162a756-6968-4530-b252-4eca9c58fe94_1024x1024.png 848w, https://substackcdn.com/image/fetch/$s_!vj1D!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd162a756-6968-4530-b252-4eca9c58fe94_1024x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!vj1D!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd162a756-6968-4530-b252-4eca9c58fe94_1024x1024.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3><strong>The Multi-Account Challenge</strong></h3><p>When you ask an AI agent &#8220;List the EKS clusters in the Prod account,&#8221; the agent needs to:</p><p>1. Determine which AWS account &#8220;Prod&#8221; refers to</p><p>2. Assume a role in that account with read permissions</p><p>3. Execute the AWS API call with the assumed credentials</p><p>4. Return the results</p><p>The obvious approach is to use `aws sts assume-role` to get temporary credentials, then use those credentials in subsequent commands. This doesn&#8217;t work with the AWS CLI MCP server because it&#8217;s stateless - each command execution creates a fresh boto3 session. The MCP server can&#8217;t &#8220;remember&#8221; credentials from a previous assume-role call.</p><blockquote><p>Bummer</p></blockquote><p>Another approach would be creating a separate MCP client instance, one per account we might need to speak to, each configured with a different AWS profile. This creates tool sprawl and of course doesn&#8217;t scale well.</p><blockquote><p>Double bummer</p></blockquote><p>What we need is <strong>profile-based role assumption</strong> where the bot includes `--profile oak` in its AWS CLI commands, and the profile configuration automatically handles assuming the correct role using the Lambda execution role as the base credential source.</p><h3><strong>Why This Is Complicated</strong></h3><p>The AWS CLI MCP server uses <strong>boto3</strong>, not shell commands. When you pass `aws eks list-clusters --profile oak`, the MCP server:</p><p>1. Parses the command string to extract the profile name</p><p>2. Creates a boto3 Session with that profile</p><p>3. Loads profile configuration from the AWS config file</p><p>4. Resolves credentials according to the profile&#8217;s settings</p><p>5. Executes the API call</p><p>Each of these steps has failure modes that produce <strong>silent errors</strong> - boto3 just fails without helpful error messages. We&#8217;ll cover the debugging later.</p><p>The key insight: the `--profile` flag <strong>does work</strong> in the AWS CLI MCP server. It extracts the profile from the parsed command and passes it to `boto3.Session(profile_name=...)`. We just need to configure profiles correctly.</p><p><strong>Lets do it! </strong></p><h1><strong>Understanding How the AWS CLI MCP Works</strong></h1><p>The first thing to understand is that the AWS CLI MCP server doesn&#8217;t shell out to the AWS CLI binary. It uses boto3 directly. This is important because it means we need to think about boto3 session management, not shell command execution.</p><p>When you send `aws eks list-clusters --profile oak` to the MCP server, heres what actually happens under the hood:</p><p>The server parses the command string and extracts the profile name from the `--profile` flag. I actually went digging through the source code to confirm this because the documentation wasn&#8217;t clear. In the parser.py file, theres code that does:</p><pre><code>profile = getattr(global_args, &#8216;profile&#8217;, None)</code></pre><p>Then in driver.py, when creating credentials:</p><pre><code>credentials = credentials or get_local_credentials(
  profile=translation.command.profile or AWS_API_MCP_PROFILE_NAME
)</code></pre><p>This means the `--profile` flag IS recognized and used. The server creates a fresh boto3 session for each command using that profile name.</p><blockquote><p>Great, so profiles work</p></blockquote><p>Now we need to understand where boto3 gets profile information from.</p><h3>AWS Config File vs Credentials File</h3><p>Boto3 looks in two places for configuration:</p><p>1. `~/.aws/credentials` - Contains access keys (AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY)</p><p>2. `~/.aws/config` - Contains profile settings like region, role_arn, and credential sources</p><p>By default, boto3 only reads the credentials file. To make it read the config file, you need to set the environment variable `AWS_SDK_LOAD_CONFIG=1`.</p><blockquote><p>This is critical and not obvious</p></blockquote><p>Our profiles with role assumption settings live in the config file, so without this environment variable, boto3 cant see them.</p><h3>Lambda Credentials and credential_source</h3><p>When your Lambda function runs, AWS provides temporary credentials via environment variables:</p><p>- AWS_ACCESS_KEY_ID</p><p>- AWS_SECRET_ACCESS_KEY</p><p>- AWS_SESSION_TOKEN</p><p>These credentials belong to the Lambda execution role. In our case, thats the role `Ue1LiVeraResearchTestWorkerRole`.</p><p>In the AWS config file, you can tell a profile to use credentials from the environment with:</p><pre><code>[profile oak]
role_arn = arn:aws:iam::123456789:role/read-only-role
credential_source = Environment</code></pre><p>This tells boto3: &#8220;Get the base credentials from environment variables, then use those to assume the role specified in role_arn.&#8221;</p><h2>The Two-Step Credential Flow</h2><p>Heres the complete flow:</p><p>1. Lambda execution role provides credentials via environment variables</p><p>2. Bot sends: `aws eks list-clusters --profile oak`</p><p>3. MCP server extracts `--profile oak` from the command</p><p>4. Boto3 creates a session with `profile_name=&#8217;prod&#8217;`</p><p>5. Boto3 reads `~/.aws/config` and finds `[profile oak]`</p><p>6. Sees `credential_source = Environment` and loads AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN from environment</p><p>7. Uses those credentials to call `sts:AssumeRole` for the role_arn</p><p>8. Gets back temporary credentials for the assumed role</p><p>9. Executes `eks:ListClusters` with the assumed role credentials</p><p>The key insight here is that credential_source = Environment lets us chain roles: Lambda role &#8594; assumed role.</p><p>What About source_profile?</p><p>You might think you could do this instead:</p><pre><code>[profile default]
credential_source = Environment
[profile oak]
role_arn = arn:aws:iam::123456789:role/read-only-role
source_profile = default</code></pre><p>This doesn&#8217;t work. When a profile has `credential_source = Environment`, it cant be used as a source_profile. AWS SDK documentation mentions this but its easy to miss.</p><blockquote><p>I spent hours debugging this</p></blockquote><p>Each profile needs its own `credential_source = Environment` setting directly. No chaining through a default profile.</p><p>Now that we understand how boto3 resolves profiles and credentials, lets build the actual implementation.</p><h1><strong>Building the Solution</strong></h1><p>We need to configure everything so that when the bot says `aws eks list-clusters --profile oak`, it automatically assumes the correct role in the Oak account and returns results.</p><p>Heres what we need to build:</p><p>1. An AWS config file with profiles for each account</p><p>2. A Dockerfile that pre-installs the MCP server and copies the config</p><p>3. A Python worker module that sets up the MCP client with correct environment variables</p><p>4. IAM permissions on both sides of the trust relationship</p><p>Lets go through each piece.</p><h3>The AWS Config File</h3><p>Create a file called `aws_config` in your Lambda project:</p><pre><code># AWS Config for Multi-Account Access

[profile oak]
role_arn = arn:aws:iam::111111111111:role/read-only-role
role_session_name = MyBot
region = us-east-1
credential_source = Environment

[profile birch]
role_arn = arn:aws:iam::222222222222:role/read-only-role
role_session_name = MyBot
region = us-east-1
credential_source = Environment

[profile palm]
role_arn = arn:aws:iam::333333333333:role/read-only-role
role_session_name = MyBot
region = us-east-1
credential_source = Environment</code></pre><p>Key points:</p><p>- Each profile is self-contained with `credential_source = Environment`</p><p>- No `[profile default]` needed</p><p>- `role_arn` points to the read-only role in each target account</p><p>- `role_session_name` can be anything - its useful for CloudTrail auditing</p><p>- Replace the account IDs with your actual account IDs</p><h3>The Dockerfile</h3><p>We need to pre-install the AWS CLI MCP server and copy our config file:</p><pre><code># Pre-install AWS CLI MCP server

RUN mkdir -p /opt/aws-cli-mcp-server &amp;&amp; \
 cd /opt/aws-cli-mcp-server &amp;&amp; \
 uv venv &amp;&amp; \
 uv pip install --python .venv/bin/python awslabs.aws-api-mcp-server &amp;&amp; \
 chmod -R a+rX /opt/aws-cli-mcp-server

# Copy AWS CLI config with pre-configured profiles
COPY aws_config /opt/aws_config</code></pre><p>Why `/opt` instead of `/tmp`? Lambda clears `/tmp` on every cold start, but I can read from `/opt`. The worker will copy it to `/tmp` at runtime since boto3 needs write access to the directory.</p><p>Also note: I tried copying the entire venv to `/tmp` and ran out of disk space. Lambda gives you 512MB in `/tmp` by default and the MCP server venv is huge. Running from `/opt` works fine.</p><h3>The Worker Module</h3><p>Create `worker_mcp_aws_cli.py`:</p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist142131122\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-aws_cli_mcp-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;aws_cli_mcp.py content, created by KyMidd on 01:54AM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;4\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;aws_cli_mcp.py\&quot;>\n        <tr>\n          <td id=\&quot;file-aws_cli_mcp-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-aws_cli_mcp-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-en>initial_aws_cli_mcp_client</span>(<span class=pl-s1>aws_region</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-aws_cli_mcp-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-aws_cli_mcp-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  ...</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-aws_cli_mcp-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-aws_cli_mcp-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  <span class=pl-s1>env</span> <span class=pl-c1>=</span> {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-aws_cli_mcp-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-aws_cli_mcp-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s>&amp;quot;HOME&amp;quot;</span>: <span class=pl-s>&amp;quot;/tmp&amp;quot;</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-aws_cli_mcp-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-aws_cli_mcp-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s>&amp;quot;AWS_REGION&amp;quot;</span>: <span class=pl-s1>aws_region</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-aws_cli_mcp-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-aws_cli_mcp-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s>&amp;quot;AWS_CONFIG_FILE&amp;quot;</span>: <span class=pl-s>&amp;quot;/tmp/.aws/config&amp;quot;</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-aws_cli_mcp-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-aws_cli_mcp-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s>&amp;quot;AWS_API_MCP_WORKING_DIR&amp;quot;</span>: <span class=pl-s>&amp;quot;/tmp/aws-mcp-working&amp;quot;</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-aws_cli_mcp-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-aws_cli_mcp-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s>&amp;quot;AWS_SDK_LOAD_CONFIG&amp;quot;</span>: <span class=pl-s>&amp;quot;1&amp;quot;</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-aws_cli_mcp-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-aws_cli_mcp-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s>&amp;quot;PYTHONUNBUFFERED&amp;quot;</span>: <span class=pl-s>&amp;quot;1&amp;quot;</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-aws_cli_mcp-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-aws_cli_mcp-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># Pass through Lambda execution role credentials</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-aws_cli_mcp-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-aws_cli_mcp-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s>&amp;quot;AWS_ACCESS_KEY_ID&amp;quot;</span>: <span class=pl-s1>os</span>.<span class=pl-c1>environ</span>.<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;AWS_ACCESS_KEY_ID&amp;quot;</span>, <span class=pl-s>&amp;quot;&amp;quot;</span>),</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-aws_cli_mcp-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-aws_cli_mcp-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s>&amp;quot;AWS_SECRET_ACCESS_KEY&amp;quot;</span>: <span class=pl-s1>os</span>.<span class=pl-c1>environ</span>.<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;AWS_SECRET_ACCESS_KEY&amp;quot;</span>, <span class=pl-s>&amp;quot;&amp;quot;</span>),</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-aws_cli_mcp-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-aws_cli_mcp-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s>&amp;quot;AWS_SESSION_TOKEN&amp;quot;</span>: <span class=pl-s1>os</span>.<span class=pl-c1>environ</span>.<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;AWS_SESSION_TOKEN&amp;quot;</span>, <span class=pl-s>&amp;quot;&amp;quot;</span>),</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-aws_cli_mcp-py-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-aws_cli_mcp-py-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    }</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-aws_cli_mcp-py-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-aws_cli_mcp-py-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-aws_cli_mcp-py-L16\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;16\&quot;></td>\n          <td id=\&quot;file-aws_cli_mcp-py-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>return</span> <span class=pl-en>MCPClient</span>(</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-aws_cli_mcp-py-L17\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;17\&quot;></td>\n          <td id=\&quot;file-aws_cli_mcp-py-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>lambda</span>: <span class=pl-en>stdio_client</span>(</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-aws_cli_mcp-py-L18\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;18\&quot;></td>\n          <td id=\&quot;file-aws_cli_mcp-py-LC18\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-en>StdioServerParameters</span>(</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-aws_cli_mcp-py-L19\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;19\&quot;></td>\n          <td id=\&quot;file-aws_cli_mcp-py-LC19\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>cwd</span><span class=pl-c1>=</span><span class=pl-s>&amp;quot;/tmp/aws-mcp-working&amp;quot;</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-aws_cli_mcp-py-L20\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;20\&quot;></td>\n          <td id=\&quot;file-aws_cli_mcp-py-LC20\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>command</span><span class=pl-c1>=</span><span class=pl-s>f&amp;quot;<span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>opt_aws_cli_mcp_dir</span><span class=pl-kos>}</span></span>/.venv/bin/awslabs.aws-api-mcp-server&amp;quot;</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-aws_cli_mcp-py-L21\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;21\&quot;></td>\n          <td id=\&quot;file-aws_cli_mcp-py-LC21\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>env</span><span class=pl-c1>=</span><span class=pl-s1>env</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-aws_cli_mcp-py-L22\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;22\&quot;></td>\n          <td id=\&quot;file-aws_cli_mcp-py-LC22\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        )</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-aws_cli_mcp-py-L23\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;23\&quot;></td>\n          <td id=\&quot;file-aws_cli_mcp-py-LC23\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    )</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-aws_cli_mcp-py-L24\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;24\&quot;></td>\n          <td id=\&quot;file-aws_cli_mcp-py-LC24\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-aws_cli_mcp-py-L25\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;25\&quot;></td>\n          <td id=\&quot;file-aws_cli_mcp-py-LC25\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-aws_cli_mcp-py-L26\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;26\&quot;></td>\n          <td id=\&quot;file-aws_cli_mcp-py-LC26\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-en>build_aws_cli_mcp_client</span>(<span class=pl-s1>aws_region</span><span class=pl-c1>=</span><span class=pl-s>&amp;quot;us-east-1&amp;quot;</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-aws_cli_mcp-py-L27\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;27\&quot;></td>\n          <td id=\&quot;file-aws_cli_mcp-py-LC27\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>aws_cli_mcp_client</span> <span class=pl-c1>=</span> <span class=pl-en>initial_aws_cli_mcp_client</span>(<span class=pl-s1>aws_region</span><span class=pl-c1>=</span><span class=pl-s1>aws_region</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-aws_cli_mcp-py-L28\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;28\&quot;></td>\n          <td id=\&quot;file-aws_cli_mcp-py-LC28\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>aws_cli_client</span> <span class=pl-c1>=</span> <span class=pl-s1>aws_cli_mcp_client</span>.<span class=pl-c1>__enter__</span>()</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-aws_cli_mcp-py-L29\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;29\&quot;></td>\n          <td id=\&quot;file-aws_cli_mcp-py-LC29\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>all_aws_cli_tools</span> <span class=pl-c1>=</span> <span class=pl-s1>aws_cli_client</span>.<span class=pl-c1>list_tools_sync</span>()</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-aws_cli_mcp-py-L30\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;30\&quot;></td>\n          <td id=\&quot;file-aws_cli_mcp-py-LC30\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-aws_cli_mcp-py-L31\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;31\&quot;></td>\n          <td id=\&quot;file-aws_cli_mcp-py-LC31\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-en>print</span>(<span class=pl-s>f&amp;quot;AWS CLI MCP: Returning <span class=pl-s1><span class=pl-kos>{</span><span class=pl-en>len</span>(<span class=pl-s1>all_aws_cli_tools</span>)<span class=pl-kos>}</span></span> tools&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-aws_cli_mcp-py-L32\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;32\&quot;></td>\n          <td id=\&quot;file-aws_cli_mcp-py-LC32\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>return</span> <span class=pl-s1>aws_cli_mcp_client</span>, <span class=pl-s1>all_aws_cli_tools</span></td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/b9d181e33601c6c9c566eab918bb46dd/raw/b02c4fb0edb920b28920767ce747bb1ccde386df/aws_cli_mcp.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/b9d181e33601c6c9c566eab918bb46dd#file-aws_cli_mcp-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          aws_cli_mcp.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-b6e2a42a64d4.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-b6e2a42a64d4.css"><div id="gist142131122" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-aws_cli_mcp-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="4" data-paste-markdown-skip="" data-tagsearch-path="aws_cli_mcp.py">
        <tbody><tr>
          <td id="file-aws_cli_mcp-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-aws_cli_mcp-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-en">initial_aws_cli_mcp_client</span>(<span class="pl-s1">aws_region</span>):</td>
        </tr>
        <tr>
          <td id="file-aws_cli_mcp-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-aws_cli_mcp-py-LC2" class="blob-code blob-code-inner js-file-line">  ...</td>
        </tr>
        <tr>
          <td id="file-aws_cli_mcp-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-aws_cli_mcp-py-LC3" class="blob-code blob-code-inner js-file-line">  <span class="pl-s1">env</span> <span class="pl-c1">=</span> {</td>
        </tr>
        <tr>
          <td id="file-aws_cli_mcp-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-aws_cli_mcp-py-LC4" class="blob-code blob-code-inner js-file-line">        <span class="pl-s">"HOME"</span>: <span class="pl-s">"/tmp"</span>,</td>
        </tr>
        <tr>
          <td id="file-aws_cli_mcp-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-aws_cli_mcp-py-LC5" class="blob-code blob-code-inner js-file-line">        <span class="pl-s">"AWS_REGION"</span>: <span class="pl-s1">aws_region</span>,</td>
        </tr>
        <tr>
          <td id="file-aws_cli_mcp-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-aws_cli_mcp-py-LC6" class="blob-code blob-code-inner js-file-line">        <span class="pl-s">"AWS_CONFIG_FILE"</span>: <span class="pl-s">"/tmp/.aws/config"</span>,</td>
        </tr>
        <tr>
          <td id="file-aws_cli_mcp-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-aws_cli_mcp-py-LC7" class="blob-code blob-code-inner js-file-line">        <span class="pl-s">"AWS_API_MCP_WORKING_DIR"</span>: <span class="pl-s">"/tmp/aws-mcp-working"</span>,</td>
        </tr>
        <tr>
          <td id="file-aws_cli_mcp-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-aws_cli_mcp-py-LC8" class="blob-code blob-code-inner js-file-line">        <span class="pl-s">"AWS_SDK_LOAD_CONFIG"</span>: <span class="pl-s">"1"</span>,</td>
        </tr>
        <tr>
          <td id="file-aws_cli_mcp-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-aws_cli_mcp-py-LC9" class="blob-code blob-code-inner js-file-line">        <span class="pl-s">"PYTHONUNBUFFERED"</span>: <span class="pl-s">"1"</span>,</td>
        </tr>
        <tr>
          <td id="file-aws_cli_mcp-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-aws_cli_mcp-py-LC10" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># Pass through Lambda execution role credentials</span></td>
        </tr>
        <tr>
          <td id="file-aws_cli_mcp-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-aws_cli_mcp-py-LC11" class="blob-code blob-code-inner js-file-line">        <span class="pl-s">"AWS_ACCESS_KEY_ID"</span>: <span class="pl-s1">os</span>.<span class="pl-c1">environ</span>.<span class="pl-c1">get</span>(<span class="pl-s">"AWS_ACCESS_KEY_ID"</span>, <span class="pl-s">""</span>),</td>
        </tr>
        <tr>
          <td id="file-aws_cli_mcp-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-aws_cli_mcp-py-LC12" class="blob-code blob-code-inner js-file-line">        <span class="pl-s">"AWS_SECRET_ACCESS_KEY"</span>: <span class="pl-s1">os</span>.<span class="pl-c1">environ</span>.<span class="pl-c1">get</span>(<span class="pl-s">"AWS_SECRET_ACCESS_KEY"</span>, <span class="pl-s">""</span>),</td>
        </tr>
        <tr>
          <td id="file-aws_cli_mcp-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-aws_cli_mcp-py-LC13" class="blob-code blob-code-inner js-file-line">        <span class="pl-s">"AWS_SESSION_TOKEN"</span>: <span class="pl-s1">os</span>.<span class="pl-c1">environ</span>.<span class="pl-c1">get</span>(<span class="pl-s">"AWS_SESSION_TOKEN"</span>, <span class="pl-s">""</span>),</td>
        </tr>
        <tr>
          <td id="file-aws_cli_mcp-py-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-aws_cli_mcp-py-LC14" class="blob-code blob-code-inner js-file-line">    }</td>
        </tr>
        <tr>
          <td id="file-aws_cli_mcp-py-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-aws_cli_mcp-py-LC15" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-aws_cli_mcp-py-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-aws_cli_mcp-py-LC16" class="blob-code blob-code-inner js-file-line"><span class="pl-k">return</span> <span class="pl-en">MCPClient</span>(</td>
        </tr>
        <tr>
          <td id="file-aws_cli_mcp-py-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-aws_cli_mcp-py-LC17" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">lambda</span>: <span class="pl-en">stdio_client</span>(</td>
        </tr>
        <tr>
          <td id="file-aws_cli_mcp-py-L18" class="blob-num js-line-number js-blob-rnum" data-line-number="18"></td>
          <td id="file-aws_cli_mcp-py-LC18" class="blob-code blob-code-inner js-file-line">        <span class="pl-en">StdioServerParameters</span>(</td>
        </tr>
        <tr>
          <td id="file-aws_cli_mcp-py-L19" class="blob-num js-line-number js-blob-rnum" data-line-number="19"></td>
          <td id="file-aws_cli_mcp-py-LC19" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">cwd</span><span class="pl-c1">=</span><span class="pl-s">"/tmp/aws-mcp-working"</span>,</td>
        </tr>
        <tr>
          <td id="file-aws_cli_mcp-py-L20" class="blob-num js-line-number js-blob-rnum" data-line-number="20"></td>
          <td id="file-aws_cli_mcp-py-LC20" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">command</span><span class="pl-c1">=</span><span class="pl-s">f"<span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">opt_aws_cli_mcp_dir</span><span class="pl-kos">}</span></span>/.venv/bin/awslabs.aws-api-mcp-server"</span>,</td>
        </tr>
        <tr>
          <td id="file-aws_cli_mcp-py-L21" class="blob-num js-line-number js-blob-rnum" data-line-number="21"></td>
          <td id="file-aws_cli_mcp-py-LC21" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">env</span><span class="pl-c1">=</span><span class="pl-s1">env</span>,</td>
        </tr>
        <tr>
          <td id="file-aws_cli_mcp-py-L22" class="blob-num js-line-number js-blob-rnum" data-line-number="22"></td>
          <td id="file-aws_cli_mcp-py-LC22" class="blob-code blob-code-inner js-file-line">        )</td>
        </tr>
        <tr>
          <td id="file-aws_cli_mcp-py-L23" class="blob-num js-line-number js-blob-rnum" data-line-number="23"></td>
          <td id="file-aws_cli_mcp-py-LC23" class="blob-code blob-code-inner js-file-line">    )</td>
        </tr>
        <tr>
          <td id="file-aws_cli_mcp-py-L24" class="blob-num js-line-number js-blob-rnum" data-line-number="24"></td>
          <td id="file-aws_cli_mcp-py-LC24" class="blob-code blob-code-inner js-file-line">)</td>
        </tr>
        <tr>
          <td id="file-aws_cli_mcp-py-L25" class="blob-num js-line-number js-blob-rnum" data-line-number="25"></td>
          <td id="file-aws_cli_mcp-py-LC25" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-aws_cli_mcp-py-L26" class="blob-num js-line-number js-blob-rnum" data-line-number="26"></td>
          <td id="file-aws_cli_mcp-py-LC26" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-en">build_aws_cli_mcp_client</span>(<span class="pl-s1">aws_region</span><span class="pl-c1">=</span><span class="pl-s">"us-east-1"</span>):</td>
        </tr>
        <tr>
          <td id="file-aws_cli_mcp-py-L27" class="blob-num js-line-number js-blob-rnum" data-line-number="27"></td>
          <td id="file-aws_cli_mcp-py-LC27" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">aws_cli_mcp_client</span> <span class="pl-c1">=</span> <span class="pl-en">initial_aws_cli_mcp_client</span>(<span class="pl-s1">aws_region</span><span class="pl-c1">=</span><span class="pl-s1">aws_region</span>)</td>
        </tr>
        <tr>
          <td id="file-aws_cli_mcp-py-L28" class="blob-num js-line-number js-blob-rnum" data-line-number="28"></td>
          <td id="file-aws_cli_mcp-py-LC28" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">aws_cli_client</span> <span class="pl-c1">=</span> <span class="pl-s1">aws_cli_mcp_client</span>.<span class="pl-c1">__enter__</span>()</td>
        </tr>
        <tr>
          <td id="file-aws_cli_mcp-py-L29" class="blob-num js-line-number js-blob-rnum" data-line-number="29"></td>
          <td id="file-aws_cli_mcp-py-LC29" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">all_aws_cli_tools</span> <span class="pl-c1">=</span> <span class="pl-s1">aws_cli_client</span>.<span class="pl-c1">list_tools_sync</span>()</td>
        </tr>
        <tr>
          <td id="file-aws_cli_mcp-py-L30" class="blob-num js-line-number js-blob-rnum" data-line-number="30"></td>
          <td id="file-aws_cli_mcp-py-LC30" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-aws_cli_mcp-py-L31" class="blob-num js-line-number js-blob-rnum" data-line-number="31"></td>
          <td id="file-aws_cli_mcp-py-LC31" class="blob-code blob-code-inner js-file-line"><span class="pl-en">print</span>(<span class="pl-s">f"AWS CLI MCP: Returning <span class="pl-s1"><span class="pl-kos">{</span><span class="pl-en">len</span>(<span class="pl-s1">all_aws_cli_tools</span>)<span class="pl-kos">}</span></span> tools"</span>)</td>
        </tr>
        <tr>
          <td id="file-aws_cli_mcp-py-L32" class="blob-num js-line-number js-blob-rnum" data-line-number="32"></td>
          <td id="file-aws_cli_mcp-py-LC32" class="blob-code blob-code-inner js-file-line"><span class="pl-k">return</span> <span class="pl-s1">aws_cli_mcp_client</span>, <span class="pl-s1">all_aws_cli_tools</span></td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/b9d181e33601c6c9c566eab918bb46dd/raw/b02c4fb0edb920b28920767ce747bb1ccde386df/aws_cli_mcp.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/b9d181e33601c6c9c566eab918bb46dd#file-aws_cli_mcp-py" class="Link--inTextBlock">
          aws_cli_mcp.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>Critical parts:</p><p>1. <strong>Copy config to /tmp</strong> - boto3 needs the config file in a writable location</p><p>2. <strong>AWS_SDK_LOAD_CONFIG=1</strong> - Without this, boto3 ignores the config file entirely</p><p>3. <strong>Pass through credentials </strong>- The subprocess doesn&#8217;t inherit environment automatically when you provide a custom env dict. You must explicitly pass AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and AWS_SESSION_TOKEN</p><blockquote><p>This one got me for like 2 hours. Most lambda processes automatically inherit the IAM role assigned to the lambda. Not this tool, BIG SIGH</p></blockquote><p>I initially didn&#8217;t pass through the credentials and boto3 just silently failed with no helpful error messages.</p><p>Integrating with Strands</p><p>In your main agent setup (like `worker_agent.py`), integrate the MCP client:</p><pre><code>from worker_mcp_aws_cli import build_aws_cli_mcp_client
# Build AWS CLI MCP client
aws_cli_mcp_client, aws_cli_tools = build_aws_cli_mcp_client(aws_region=&#8221;us-east-1&#8221;)
# Prefix tool names to avoid collisions
aws_cli_tools = add_prefix_to_mcp_tools(aws_cli_tools, &#8220;aws&#8221;)
# Add to agent tools
tools.extend(aws_cli_tools)</code></pre><p>The tool prefixing gives you `aws_call_aws` and `aws_suggest_aws_commands` instead of just `call_aws`. This prevents collisions if you have multiple MCP servers.</p><p>IAM Permissions - Both Sides Must Agree</p><p>For cross-account role assumption to work, you need permissions configured on both sides. </p><p>So the IAM role you assign to the lambda must permit assuming an IAM role, and the IAM role must permit you to assume it. If either is wrong, the assuming doesn&#8217;t work. </p><blockquote><p>This is hard enough to debug in a regular application, but a HUGE pain when your AI eats errors and doesn&#8217;t print them</p></blockquote><h3>Testing It</h3><p>At this point, you should be able to deploy your Lambda and test:</p><p>Bot receives: &#8220;List EKS clusters in the Oak account&#8221;</p><p>Bot constructs: `aws eks list-clusters --region us-east-1 --profile oak`</p><p>MCP server:</p><p>1. Parses `--profile oak`</p><p>2. Loads `/tmp/.aws/config`</p><p>3. Finds `[profile oak]` with `credential_source = Environment`</p><p>4. Gets Lambda credentials from environment variables</p><p>5. Calls `sts:AssumeRole` for `arn:aws:iam::111111111111:role/read-only-role`</p><p>6. Gets temporary credentials</p><p>7. Executes `eks:ListClusters` with assumed credentials</p><p>8. Returns results</p><blockquote><p>Beautiful</p></blockquote><p>Except when it doesn&#8217;t work. Lets talk about debugging.</p><h1><strong>The Debugging Journey</strong></h1><p>When I first deployed this, nothing worked. The bot would try to list resources and just get generic &#8220;Unable to locate credentials&#8221; errors. Not helpful.</p><p>The problem with boto3 and the AWS SDK is that errors are often silent or misleading. A missing environment variable produces the same error as a malformed config file or insufficient IAM permissions.</p><h3>Failure 1: boto3 Cant Find the Config File</h3><p>First test: bot tries `aws eks list-clusters --profile oak`.</p><p>Error: `Unable to locate credentials. You can configure credentials by running &#8220;aws configure&#8221;.`</p><p>This error is lying. The credentials exist (Lambda execution role). The real problem: boto3 isn&#8217;t reading the config file at all.</p><p>I added debug logging to print the contents of `/tmp/.aws/config` right after copying it. The file was there. The profiles were correct. boto3 just wasnt looking at it.</p><p>The fix: `AWS_SDK_LOAD_CONFIG=1`</p><p>Without this environment variable, boto3 ONLY reads `~/.aws/credentials` and completely ignores `~/.aws/config`. Since our profiles live in the config file, boto3 had no idea they existed.</p><p>After adding that one line to the env dict, profiles started working.</p><blockquote><p>One down</p></blockquote><h3>Failure 2: Subprocess Cant See Lambda Credentials</h3><p>Second test after adding `AWS_SDK_LOAD_CONFIG=1`: same error.</p><p>Wait, what? I just fixed this.</p><p>Turns out when you pass a custom `env` dictionary to `StdioServerParameters`, Python doesn&#8217;t merge it with the parent process environment. It REPLACES the environment entirely.</p><p>The MCP server subprocess was starting with ONLY the variables I explicitly provided:</p><p>- HOME</p><p>- AWS_REGION</p><p>- AWS_CONFIG_FILE</p><p>- AWS_SDK_LOAD_CONFIG</p><p>- AWS_API_MCP_WORKING_DIR</p><blockquote><p>Missing: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN</p></blockquote><p>Lambda provides these credentials to the parent process, but the subprocess couldnt see them. So when boto3 tried to read `credential_source = Environment`, there were no credentials in the environment.</p><p>The fix: explicitly pass through the credentials</p><pre><code>env = {
# ... other variables ...
&#8220;AWS_ACCESS_KEY_ID&#8221;: os.environ.get(&#8221;AWS_ACCESS_KEY_ID&#8221;, &#8220;&#8221;),
&#8220;AWS_SECRET_ACCESS_KEY&#8221;: os.environ.get(&#8221;AWS_SECRET_ACCESS_KEY&#8221;, &#8220;&#8221;),
&#8220;AWS_SESSION_TOKEN&#8221;: os.environ.get(&#8221;AWS_SESSION_TOKEN&#8221;, &#8220;&#8221;),
}</code></pre><p>This grabs the credentials from the parent Lambda environment and explicitly passes them to the MCP server subprocess.</p><p>After this change, role assumption started working.</p><blockquote><p>Two down</p></blockquote><h3>Debugging Tips</h3><p>If youre building something similar, these techniques helped me:</p><p>1. <strong>Add debug logging everywhere </strong>- Print environment variables (redact sensitive values), print file contents, print boto3 session configuration</p><p>2. <strong>Test incrementally</strong> - I tested after each change: config file copied? Environment variable set? Credentials passed through?</p><p>3. <strong>Read the source code</strong> - The AWS CLI MCP server is open source. When documentation was unclear, I read driver.py and parser.py directly</p><p>4. <strong>Use CloudTrail</strong> - Failed AssumeRole attempts show up in CloudTrail in the target account with detailed error messages</p><p>The key lesson: boto3 fails silently. You need to instrument your code with logging to understand whats actually happening.</p><h1><strong>Summary</strong></h1><p>So thats it. We now have a single AI agent running in Lambda that can query resources across any number of AWS accounts using profile-based role assumption.</p><p>The AWS CLI MCP server does support --profile flags, even though its not well documented. You just need to configure everything correctly for boto3 to work.</p><p>The full code is in my <a href="https://github.com/KyMidd/SlackStrandsAgenticBot">GitHub/KyMidd/SlackStrandsAgenticBot</a> repo if you want to see the complete implementation.</p><p>Now my bot can answer &#8220;How many EKS clusters are in the Oak account?&#8221; and actually get the answer. Pretty cool.</p><p>Good luck out there. <br>kyler</p>]]></content:encoded></item><item><title><![CDATA[🔥Fixing MCP Tool Name Collisions When Using Multiple MCP Servers]]></title><description><![CDATA[aka: Why does every MCP server define "search" as a tool?]]></description><link>https://www.letsdodevops.com/p/fixing-mcp-tool-name-collisions-when</link><guid isPermaLink="false">https://www.letsdodevops.com/p/fixing-mcp-tool-name-collisions-when</guid><dc:creator><![CDATA[Kyler Middleton]]></dc:creator><pubDate>Tue, 28 Oct 2025 14:31:26 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!_6pq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbfba7f3-0623-4f1b-95c3-0fbe93ca05d7_909x1160.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote><p><em>This blog series focuses on presenting complex DevOps projects as simple and approachable via plain language and lots of pictures. You can do it!</em></p><p>These articles are supported by readers, <a href="https://www.letsdodevops.com/subscribe">please consider subscribing</a> to support me writing more of these articles &lt;3 :)</p></blockquote><p>Hey all! </p><p>I&#8217;ve been building out my <a href="https://github.com/KyMidd/SlackStrandsAgenticBot">Slack Strands Agentic bot</a>, a bot that comes with built-in MCPs that could connect to: </p><ul><li><p>PagerDuty</p></li><li><p>Confluence / Jira</p></li><li><p>GitHub</p></li></ul><p>As well as read our Bedrock knowledge base. I want the bot to be able to read the real status of resources, like in AWS or Azure. </p><p>When I added the Azure MCP, I worked through the bugs, and when I got it right&#8230; it crashed. </p><pre><code>ERROR: Tool name &#8216;search&#8217; already exists</code></pre><p>MCP (Model Context Protocol) is how AI agents connect to external tools. It&#8217;s basically an API, but instead of you calling endpoints, the agent does. You give it access to a GitHub MCP server, and suddenly it can search code, open issues, list repos. Same with Atlassian - it gets tools for Confluence and Jira. Add PagerDuty and Azure, and you&#8217;ve got a pretty capable agent.</p><p>Except when two servers both provide a tool called <code>search</code>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!_6pq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbfba7f3-0623-4f1b-95c3-0fbe93ca05d7_909x1160.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!_6pq!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbfba7f3-0623-4f1b-95c3-0fbe93ca05d7_909x1160.png 424w, https://substackcdn.com/image/fetch/$s_!_6pq!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbfba7f3-0623-4f1b-95c3-0fbe93ca05d7_909x1160.png 848w, https://substackcdn.com/image/fetch/$s_!_6pq!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbfba7f3-0623-4f1b-95c3-0fbe93ca05d7_909x1160.png 1272w, https://substackcdn.com/image/fetch/$s_!_6pq!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbfba7f3-0623-4f1b-95c3-0fbe93ca05d7_909x1160.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!_6pq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbfba7f3-0623-4f1b-95c3-0fbe93ca05d7_909x1160.png" width="450" height="574.2574257425742" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fbfba7f3-0623-4f1b-95c3-0fbe93ca05d7_909x1160.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1160,&quot;width&quot;:909,&quot;resizeWidth&quot;:450,&quot;bytes&quot;:1224410,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.letsdodevops.com/i/176422424?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c1edd1d-afc1-469d-a719-767fa190b975_1024x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!_6pq!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbfba7f3-0623-4f1b-95c3-0fbe93ca05d7_909x1160.png 424w, https://substackcdn.com/image/fetch/$s_!_6pq!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbfba7f3-0623-4f1b-95c3-0fbe93ca05d7_909x1160.png 848w, https://substackcdn.com/image/fetch/$s_!_6pq!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbfba7f3-0623-4f1b-95c3-0fbe93ca05d7_909x1160.png 1272w, https://substackcdn.com/image/fetch/$s_!_6pq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbfba7f3-0623-4f1b-95c3-0fbe93ca05d7_909x1160.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Apparently when you import tools to your bots, if the tool names are identical, it crashes the bot (at least for the Strands library). </p><p>Atlassian MCP has search for finding Confluence pages, and Azure MCP has search for Azure resources. Both correct, and both needed, but both have the same name. </p><p>It&#8217;s apparently not like variable scoping where context matters. This is a flat namespace. </p><p>I debugged the tools that are registering, you can see them below. </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist141860516\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-mcp-tool-collision-logs-txt\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-text  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;mcp-tool-collision-logs.txt content, created by KyMidd on 05:26PM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;4\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;mcp-tool-collision-logs.txt\&quot;>\n        <tr>\n          <td id=\&quot;file-mcp-tool-collision-logs-txt-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-mcp-tool-collision-logs-txt-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>GitHub MCP: Returning 25 tools: [&amp;#39;get_commit&amp;#39;, &amp;#39;get_file_contents&amp;#39;, &amp;#39;get_issue&amp;#39;, &amp;#39;get_issue_comments&amp;#39;, &amp;#39;get_label&amp;#39;, &amp;#39;get_latest_release&amp;#39;, &amp;#39;get_me&amp;#39;, &amp;#39;get_release_by_tag&amp;#39;, &amp;#39;get_tag&amp;#39;, &amp;#39;get_team_members&amp;#39;, &amp;#39;get_teams&amp;#39;, &amp;#39;list_branches&amp;#39;, &amp;#39;list_commits&amp;#39;, &amp;#39;list_issue_types&amp;#39;, &amp;#39;list_issues&amp;#39;, &amp;#39;list_label&amp;#39;, &amp;#39;list_pull_requests&amp;#39;, &amp;#39;list_releases&amp;#39;, &amp;#39;list_sub_issues&amp;#39;, &amp;#39;list_tags&amp;#39;, &amp;#39;search_code&amp;#39;, &amp;#39;search_issues&amp;#39;, &amp;#39;search_pull_requests&amp;#39;, &amp;#39;search_repositories&amp;#39;, &amp;#39;search_users&amp;#39;]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-mcp-tool-collision-logs-txt-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-mcp-tool-collision-logs-txt-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-mcp-tool-collision-logs-txt-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-mcp-tool-collision-logs-txt-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>Atlassian MCP: Returning 19 tools: [&amp;#39;atlassianUserInfo&amp;#39;, &amp;#39;getAccessibleAtlassianResources&amp;#39;, &amp;#39;getConfluenceSpaces&amp;#39;, &amp;#39;getConfluencePage&amp;#39;, &amp;#39;getPagesInConfluenceSpace&amp;#39;, &amp;#39;getConfluencePageFooterComments&amp;#39;, &amp;#39;getConfluencePageInlineComments&amp;#39;, &amp;#39;getConfluencePageDescendants&amp;#39;, &amp;#39;searchConfluenceUsingCql&amp;#39;, &amp;#39;getJiraIssue&amp;#39;, &amp;#39;getTransitionsForJiraIssue&amp;#39;, &amp;#39;lookupJiraAccountId&amp;#39;, &amp;#39;searchJiraIssuesUsingJql&amp;#39;, &amp;#39;getJiraIssueRemoteIssueLinks&amp;#39;, &amp;#39;getVisibleJiraProjects&amp;#39;, &amp;#39;getJiraProjectIssueTypesMetadata&amp;#39;, &amp;#39;getJiraIssueTypeMetaWithFields&amp;#39;, &amp;#39;search&amp;#39;, &amp;#39;fetch&amp;#39;]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-mcp-tool-collision-logs-txt-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-mcp-tool-collision-logs-txt-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-mcp-tool-collision-logs-txt-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-mcp-tool-collision-logs-txt-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>PagerDuty MCP: Returning 15 tools: [&amp;#39;list_incidents&amp;#39;, &amp;#39;get_incident&amp;#39;, &amp;#39;list_services&amp;#39;, &amp;#39;get_service&amp;#39;, &amp;#39;list_teams&amp;#39;, &amp;#39;get_team&amp;#39;, &amp;#39;list_team_members&amp;#39;, &amp;#39;get_user_data&amp;#39;, &amp;#39;list_users&amp;#39;, &amp;#39;list_schedules&amp;#39;, &amp;#39;get_schedule&amp;#39;, &amp;#39;list_schedule_users&amp;#39;, &amp;#39;list_oncalls&amp;#39;, &amp;#39;list_escalation_policies&amp;#39;, &amp;#39;get_escalation_policy&amp;#39;]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-mcp-tool-collision-logs-txt-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-mcp-tool-collision-logs-txt-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-mcp-tool-collision-logs-txt-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-mcp-tool-collision-logs-txt-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>Azure MCP: Returning 47 tools: [&amp;#39;documentation&amp;#39;, &amp;#39;azd&amp;#39;, &amp;#39;get_bestpractices&amp;#39;, &amp;#39;aks&amp;#39;, &amp;#39;appconfig&amp;#39;, &amp;#39;applens&amp;#39;, &amp;#39;appservice&amp;#39;, &amp;#39;role&amp;#39;, &amp;#39;datadog&amp;#39;, &amp;#39;managedlustre&amp;#39;, &amp;#39;azureterraformbestpractices&amp;#39;, &amp;#39;deploy&amp;#39;, &amp;#39;eventgrid&amp;#39;, &amp;#39;acr&amp;#39;, &amp;#39;bicepschema&amp;#39;, &amp;#39;cosmos&amp;#39;, &amp;#39;cloudarchitect&amp;#39;, &amp;#39;confidentialledger&amp;#39;, &amp;#39;eventhubs&amp;#39;, &amp;#39;foundry&amp;#39;, &amp;#39;functionapp&amp;#39;, &amp;#39;grafana&amp;#39;, &amp;#39;keyvault&amp;#39;, &amp;#39;kusto&amp;#39;, &amp;#39;loadtesting&amp;#39;, &amp;#39;marketplace&amp;#39;, &amp;#39;quota&amp;#39;, &amp;#39;monitor&amp;#39;, &amp;#39;applicationinsights&amp;#39;, &amp;#39;mysql&amp;#39;, &amp;#39;postgres&amp;#39;, &amp;#39;redis&amp;#39;, &amp;#39;communication&amp;#39;, &amp;#39;resourcehealth&amp;#39;, &amp;#39;search&amp;#39;, &amp;#39;speech&amp;#39;, &amp;#39;servicebus&amp;#39;, &amp;#39;signalr&amp;#39;, &amp;#39;sql&amp;#39;, &amp;#39;storage&amp;#39;, &amp;#39;virtualdesktop&amp;#39;, &amp;#39;workbooks&amp;#39;, &amp;#39;group_list&amp;#39;, &amp;#39;subscription_list&amp;#39;, &amp;#39;extension_azqr&amp;#39;, &amp;#39;extension_cli_generate&amp;#39;, &amp;#39;extension_cli_install&amp;#39;]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-mcp-tool-collision-logs-txt-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-mcp-tool-collision-logs-txt-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-mcp-tool-collision-logs-txt-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-mcp-tool-collision-logs-txt-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>ERROR: Tool name &amp;#39;search&amp;#39; already exists</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/18388f561c8b654512b4c0f315090f94/raw/3e02b8b6cc9e36a3c160796a34fbe516cd8bd5ff/mcp-tool-collision-logs.txt\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/18388f561c8b654512b4c0f315090f94#file-mcp-tool-collision-logs-txt\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          mcp-tool-collision-logs.txt\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-b6e2a42a64d4.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-b6e2a42a64d4.css"><div id="gist141860516" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-mcp-tool-collision-logs-txt" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-text  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="4" data-paste-markdown-skip="" data-tagsearch-path="mcp-tool-collision-logs.txt">
        <tbody><tr>
          <td id="file-mcp-tool-collision-logs-txt-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-mcp-tool-collision-logs-txt-LC1" class="blob-code blob-code-inner js-file-line">GitHub MCP: Returning 25 tools: ['get_commit', 'get_file_contents', 'get_issue', 'get_issue_comments', 'get_label', 'get_latest_release', 'get_me', 'get_release_by_tag', 'get_tag', 'get_team_members', 'get_teams', 'list_branches', 'list_commits', 'list_issue_types', 'list_issues', 'list_label', 'list_pull_requests', 'list_releases', 'list_sub_issues', 'list_tags', 'search_code', 'search_issues', 'search_pull_requests', 'search_repositories', 'search_users']</td>
        </tr>
        <tr>
          <td id="file-mcp-tool-collision-logs-txt-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-mcp-tool-collision-logs-txt-LC2" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-mcp-tool-collision-logs-txt-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-mcp-tool-collision-logs-txt-LC3" class="blob-code blob-code-inner js-file-line">Atlassian MCP: Returning 19 tools: ['atlassianUserInfo', 'getAccessibleAtlassianResources', 'getConfluenceSpaces', 'getConfluencePage', 'getPagesInConfluenceSpace', 'getConfluencePageFooterComments', 'getConfluencePageInlineComments', 'getConfluencePageDescendants', 'searchConfluenceUsingCql', 'getJiraIssue', 'getTransitionsForJiraIssue', 'lookupJiraAccountId', 'searchJiraIssuesUsingJql', 'getJiraIssueRemoteIssueLinks', 'getVisibleJiraProjects', 'getJiraProjectIssueTypesMetadata', 'getJiraIssueTypeMetaWithFields', 'search', 'fetch']</td>
        </tr>
        <tr>
          <td id="file-mcp-tool-collision-logs-txt-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-mcp-tool-collision-logs-txt-LC4" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-mcp-tool-collision-logs-txt-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-mcp-tool-collision-logs-txt-LC5" class="blob-code blob-code-inner js-file-line">PagerDuty MCP: Returning 15 tools: ['list_incidents', 'get_incident', 'list_services', 'get_service', 'list_teams', 'get_team', 'list_team_members', 'get_user_data', 'list_users', 'list_schedules', 'get_schedule', 'list_schedule_users', 'list_oncalls', 'list_escalation_policies', 'get_escalation_policy']</td>
        </tr>
        <tr>
          <td id="file-mcp-tool-collision-logs-txt-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-mcp-tool-collision-logs-txt-LC6" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-mcp-tool-collision-logs-txt-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-mcp-tool-collision-logs-txt-LC7" class="blob-code blob-code-inner js-file-line">Azure MCP: Returning 47 tools: ['documentation', 'azd', 'get_bestpractices', 'aks', 'appconfig', 'applens', 'appservice', 'role', 'datadog', 'managedlustre', 'azureterraformbestpractices', 'deploy', 'eventgrid', 'acr', 'bicepschema', 'cosmos', 'cloudarchitect', 'confidentialledger', 'eventhubs', 'foundry', 'functionapp', 'grafana', 'keyvault', 'kusto', 'loadtesting', 'marketplace', 'quota', 'monitor', 'applicationinsights', 'mysql', 'postgres', 'redis', 'communication', 'resourcehealth', 'search', 'speech', 'servicebus', 'signalr', 'sql', 'storage', 'virtualdesktop', 'workbooks', 'group_list', 'subscription_list', 'extension_azqr', 'extension_cli_generate', 'extension_cli_install']</td>
        </tr>
        <tr>
          <td id="file-mcp-tool-collision-logs-txt-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-mcp-tool-collision-logs-txt-LC8" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-mcp-tool-collision-logs-txt-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-mcp-tool-collision-logs-txt-LC9" class="blob-code blob-code-inner js-file-line">ERROR: Tool name 'search' already exists</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/18388f561c8b654512b4c0f315090f94/raw/3e02b8b6cc9e36a3c160796a34fbe516cd8bd5ff/mcp-tool-collision-logs.txt" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/18388f561c8b654512b4c0f315090f94#file-mcp-tool-collision-logs-txt" class="Link--inTextBlock">
          mcp-tool-collision-logs.txt
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>Both Atlassian and the new Azure MCP try to register a tool called &#8220;search&#8221;, which isn&#8217;t permitted. </p><p>When you connect multiple independent MCP servers to the same agent, eventually their tool names will overlap. The more servers you add, the more likely it becomes.</p><p>I don&#8217;t want to prune back those tools, I want all of them! Which meant I needed to fix the collision without modifying the MCP servers themselves (they&#8217;re first-party remote and hosted by the companies) and without breaking how the Strands SDK registers tools.</p><p>Cool! Lets do this. </p><h1><strong>The Solution: Wrap It, Prefix It, Ship It</strong></h1><p>How most tools handle this is namespaces. If a tool is imported from Azure called &#8220;search&#8221;, call it &#8220;azure_search&#8221;. That way collisions should be much more rare. And that&#8217;s what I built. </p><p>I expected I could just pass an argument to the Strands agent when I give it tools, and it&#8217;ll ingest the tool names with a prefix. </p><blockquote><p>I&#8217;m working on a PR for Strands to do this. </p></blockquote><p>We need a wrapper! Something that sits between the MCP tool and the Strands registry, changes the name, but otherwise acts exactly like the original tool.</p><p>Here&#8217;s what I built:</p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist141861391\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-prefixed_mcp_tool-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;prefixed_mcp_tool.py content, created by KyMidd on 06:00PM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;4\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;prefixed_mcp_tool.py\&quot;>\n        <tr>\n          <td id=\&quot;file-prefixed_mcp_tool-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-prefixed_mcp_tool-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>class</span> <span class=pl-v>PrefixedMCPTool</span>(<span class=pl-v>AgentTool</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-prefixed_mcp_tool-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-prefixed_mcp_tool-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s>&amp;quot;&amp;quot;&amp;quot;Wrapper that adds a prefix to an MCP tool&amp;#39;s name.&amp;quot;&amp;quot;&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-prefixed_mcp_tool-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-prefixed_mcp_tool-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>def</span> <span class=pl-en>__init__</span>(<span class=pl-s1>self</span>, <span class=pl-s1>tool</span>, <span class=pl-s1>prefix</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-prefixed_mcp_tool-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-prefixed_mcp_tool-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-en>super</span>().<span class=pl-c1>__init__</span>()</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-prefixed_mcp_tool-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-prefixed_mcp_tool-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>self</span>.<span class=pl-c1>_original_tool</span> <span class=pl-c1>=</span> <span class=pl-s1>tool</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-prefixed_mcp_tool-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-prefixed_mcp_tool-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>self</span>.<span class=pl-c1>_prefix</span> <span class=pl-c1>=</span> <span class=pl-s1>prefix</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-prefixed_mcp_tool-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-prefixed_mcp_tool-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>original_spec</span> <span class=pl-c1>=</span> <span class=pl-s1>tool</span>.<span class=pl-c1>tool_spec</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-prefixed_mcp_tool-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-prefixed_mcp_tool-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>self</span>.<span class=pl-c1>_prefixed_spec</span> <span class=pl-c1>=</span> <span class=pl-s1>original_spec</span>.<span class=pl-c1>copy</span>()</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-prefixed_mcp_tool-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-prefixed_mcp_tool-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>self</span>.<span class=pl-c1>_prefixed_spec</span>[<span class=pl-s>&amp;quot;name&amp;quot;</span>] <span class=pl-c1>=</span> <span class=pl-s>f&amp;quot;<span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>prefix</span><span class=pl-kos>}</span></span><span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>original_spec</span>[<span class=pl-s>&amp;#39;name&amp;#39;</span>]<span class=pl-kos>}</span></span>&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-prefixed_mcp_tool-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-prefixed_mcp_tool-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-en>@<span class=pl-s1>property</span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-prefixed_mcp_tool-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-prefixed_mcp_tool-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>def</span> <span class=pl-en>tool_spec</span>(<span class=pl-s1>self</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-prefixed_mcp_tool-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-prefixed_mcp_tool-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s>&amp;quot;&amp;quot;&amp;quot;Return the modified tool spec with prefixed name&amp;quot;&amp;quot;&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-prefixed_mcp_tool-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-prefixed_mcp_tool-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>return</span> <span class=pl-s1>self</span>.<span class=pl-c1>_prefixed_spec</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-prefixed_mcp_tool-py-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-prefixed_mcp_tool-py-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-en>@<span class=pl-s1>property</span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-prefixed_mcp_tool-py-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-prefixed_mcp_tool-py-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>def</span> <span class=pl-en>tool_name</span>(<span class=pl-s1>self</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-prefixed_mcp_tool-py-L16\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;16\&quot;></td>\n          <td id=\&quot;file-prefixed_mcp_tool-py-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s>&amp;quot;&amp;quot;&amp;quot;Return the prefixed tool name&amp;quot;&amp;quot;&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-prefixed_mcp_tool-py-L17\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;17\&quot;></td>\n          <td id=\&quot;file-prefixed_mcp_tool-py-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>return</span> <span class=pl-s1>self</span>.<span class=pl-c1>_prefixed_spec</span>[<span class=pl-s>&amp;quot;name&amp;quot;</span>]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-prefixed_mcp_tool-py-L18\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;18\&quot;></td>\n          <td id=\&quot;file-prefixed_mcp_tool-py-LC18\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-en>@<span class=pl-s1>property</span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-prefixed_mcp_tool-py-L19\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;19\&quot;></td>\n          <td id=\&quot;file-prefixed_mcp_tool-py-LC19\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>def</span> <span class=pl-en>tool_type</span>(<span class=pl-s1>self</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-prefixed_mcp_tool-py-L20\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;20\&quot;></td>\n          <td id=\&quot;file-prefixed_mcp_tool-py-LC20\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s>&amp;quot;&amp;quot;&amp;quot;Delegate to the original tool&amp;#39;s type&amp;quot;&amp;quot;&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-prefixed_mcp_tool-py-L21\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;21\&quot;></td>\n          <td id=\&quot;file-prefixed_mcp_tool-py-LC21\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>return</span> <span class=pl-s1>self</span>.<span class=pl-c1>_original_tool</span>.<span class=pl-c1>tool_type</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-prefixed_mcp_tool-py-L22\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;22\&quot;></td>\n          <td id=\&quot;file-prefixed_mcp_tool-py-LC22\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>def</span> <span class=pl-en>stream</span>(<span class=pl-s1>self</span>, <span class=pl-s1>tool_use</span>, <span class=pl-c1>*</span><span class=pl-s1>args</span>, <span class=pl-c1>**</span><span class=pl-s1>kwargs</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-prefixed_mcp_tool-py-L23\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;23\&quot;></td>\n          <td id=\&quot;file-prefixed_mcp_tool-py-LC23\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s>&amp;quot;&amp;quot;&amp;quot;Delegate to the original tool with all arguments</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-prefixed_mcp_tool-py-L24\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;24\&quot;></td>\n          <td id=\&quot;file-prefixed_mcp_tool-py-LC24\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s>        Important: The *args and **kwargs are crucial here!</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-prefixed_mcp_tool-py-L25\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;25\&quot;></td>\n          <td id=\&quot;file-prefixed_mcp_tool-py-LC25\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s>        The Strands framework passes additional arguments like </span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-prefixed_mcp_tool-py-L26\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;26\&quot;></td>\n          <td id=\&quot;file-prefixed_mcp_tool-py-LC26\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s>        invocation_state that must be forwarded to the original tool.</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-prefixed_mcp_tool-py-L27\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;27\&quot;></td>\n          <td id=\&quot;file-prefixed_mcp_tool-py-LC27\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s>        &amp;quot;&amp;quot;&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-prefixed_mcp_tool-py-L28\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;28\&quot;></td>\n          <td id=\&quot;file-prefixed_mcp_tool-py-LC28\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>return</span> <span class=pl-s1>self</span>.<span class=pl-c1>_original_tool</span>.<span class=pl-c1>stream</span>(<span class=pl-s1>tool_use</span>, <span class=pl-c1>*</span><span class=pl-s1>args</span>, <span class=pl-c1>**</span><span class=pl-s1>kwargs</span>)</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/8df8b0d2e4ddb734ff2126e3e5f57576/raw/47635481f5835bc2642c55b30471c10fdaccde8e/prefixed_mcp_tool.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/8df8b0d2e4ddb734ff2126e3e5f57576#file-prefixed_mcp_tool-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          prefixed_mcp_tool.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-b6e2a42a64d4.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-b6e2a42a64d4.css"><div id="gist141861391" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-prefixed_mcp_tool-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="4" data-paste-markdown-skip="" data-tagsearch-path="prefixed_mcp_tool.py">
        <tbody><tr>
          <td id="file-prefixed_mcp_tool-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-prefixed_mcp_tool-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">class</span> <span class="pl-v">PrefixedMCPTool</span>(<span class="pl-v">AgentTool</span>):</td>
        </tr>
        <tr>
          <td id="file-prefixed_mcp_tool-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-prefixed_mcp_tool-py-LC2" class="blob-code blob-code-inner js-file-line">    <span class="pl-s">"""Wrapper that adds a prefix to an MCP tool's name."""</span></td>
        </tr>
        <tr>
          <td id="file-prefixed_mcp_tool-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-prefixed_mcp_tool-py-LC3" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">def</span> <span class="pl-en">__init__</span>(<span class="pl-s1">self</span>, <span class="pl-s1">tool</span>, <span class="pl-s1">prefix</span>):</td>
        </tr>
        <tr>
          <td id="file-prefixed_mcp_tool-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-prefixed_mcp_tool-py-LC4" class="blob-code blob-code-inner js-file-line">        <span class="pl-en">super</span>().<span class="pl-c1">__init__</span>()</td>
        </tr>
        <tr>
          <td id="file-prefixed_mcp_tool-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-prefixed_mcp_tool-py-LC5" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">self</span>.<span class="pl-c1">_original_tool</span> <span class="pl-c1">=</span> <span class="pl-s1">tool</span></td>
        </tr>
        <tr>
          <td id="file-prefixed_mcp_tool-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-prefixed_mcp_tool-py-LC6" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">self</span>.<span class="pl-c1">_prefix</span> <span class="pl-c1">=</span> <span class="pl-s1">prefix</span></td>
        </tr>
        <tr>
          <td id="file-prefixed_mcp_tool-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-prefixed_mcp_tool-py-LC7" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">original_spec</span> <span class="pl-c1">=</span> <span class="pl-s1">tool</span>.<span class="pl-c1">tool_spec</span></td>
        </tr>
        <tr>
          <td id="file-prefixed_mcp_tool-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-prefixed_mcp_tool-py-LC8" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">self</span>.<span class="pl-c1">_prefixed_spec</span> <span class="pl-c1">=</span> <span class="pl-s1">original_spec</span>.<span class="pl-c1">copy</span>()</td>
        </tr>
        <tr>
          <td id="file-prefixed_mcp_tool-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-prefixed_mcp_tool-py-LC9" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">self</span>.<span class="pl-c1">_prefixed_spec</span>[<span class="pl-s">"name"</span>] <span class="pl-c1">=</span> <span class="pl-s">f"<span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">prefix</span><span class="pl-kos">}</span></span><span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">original_spec</span>[<span class="pl-s">'name'</span>]<span class="pl-kos">}</span></span>"</span></td>
        </tr>
        <tr>
          <td id="file-prefixed_mcp_tool-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-prefixed_mcp_tool-py-LC10" class="blob-code blob-code-inner js-file-line">    <span class="pl-en">@<span class="pl-s1">property</span></span></td>
        </tr>
        <tr>
          <td id="file-prefixed_mcp_tool-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-prefixed_mcp_tool-py-LC11" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">def</span> <span class="pl-en">tool_spec</span>(<span class="pl-s1">self</span>):</td>
        </tr>
        <tr>
          <td id="file-prefixed_mcp_tool-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-prefixed_mcp_tool-py-LC12" class="blob-code blob-code-inner js-file-line">        <span class="pl-s">"""Return the modified tool spec with prefixed name"""</span></td>
        </tr>
        <tr>
          <td id="file-prefixed_mcp_tool-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-prefixed_mcp_tool-py-LC13" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">return</span> <span class="pl-s1">self</span>.<span class="pl-c1">_prefixed_spec</span></td>
        </tr>
        <tr>
          <td id="file-prefixed_mcp_tool-py-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-prefixed_mcp_tool-py-LC14" class="blob-code blob-code-inner js-file-line">    <span class="pl-en">@<span class="pl-s1">property</span></span></td>
        </tr>
        <tr>
          <td id="file-prefixed_mcp_tool-py-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-prefixed_mcp_tool-py-LC15" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">def</span> <span class="pl-en">tool_name</span>(<span class="pl-s1">self</span>):</td>
        </tr>
        <tr>
          <td id="file-prefixed_mcp_tool-py-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-prefixed_mcp_tool-py-LC16" class="blob-code blob-code-inner js-file-line">        <span class="pl-s">"""Return the prefixed tool name"""</span></td>
        </tr>
        <tr>
          <td id="file-prefixed_mcp_tool-py-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-prefixed_mcp_tool-py-LC17" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">return</span> <span class="pl-s1">self</span>.<span class="pl-c1">_prefixed_spec</span>[<span class="pl-s">"name"</span>]</td>
        </tr>
        <tr>
          <td id="file-prefixed_mcp_tool-py-L18" class="blob-num js-line-number js-blob-rnum" data-line-number="18"></td>
          <td id="file-prefixed_mcp_tool-py-LC18" class="blob-code blob-code-inner js-file-line">    <span class="pl-en">@<span class="pl-s1">property</span></span></td>
        </tr>
        <tr>
          <td id="file-prefixed_mcp_tool-py-L19" class="blob-num js-line-number js-blob-rnum" data-line-number="19"></td>
          <td id="file-prefixed_mcp_tool-py-LC19" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">def</span> <span class="pl-en">tool_type</span>(<span class="pl-s1">self</span>):</td>
        </tr>
        <tr>
          <td id="file-prefixed_mcp_tool-py-L20" class="blob-num js-line-number js-blob-rnum" data-line-number="20"></td>
          <td id="file-prefixed_mcp_tool-py-LC20" class="blob-code blob-code-inner js-file-line">        <span class="pl-s">"""Delegate to the original tool's type"""</span></td>
        </tr>
        <tr>
          <td id="file-prefixed_mcp_tool-py-L21" class="blob-num js-line-number js-blob-rnum" data-line-number="21"></td>
          <td id="file-prefixed_mcp_tool-py-LC21" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">return</span> <span class="pl-s1">self</span>.<span class="pl-c1">_original_tool</span>.<span class="pl-c1">tool_type</span></td>
        </tr>
        <tr>
          <td id="file-prefixed_mcp_tool-py-L22" class="blob-num js-line-number js-blob-rnum" data-line-number="22"></td>
          <td id="file-prefixed_mcp_tool-py-LC22" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">def</span> <span class="pl-en">stream</span>(<span class="pl-s1">self</span>, <span class="pl-s1">tool_use</span>, <span class="pl-c1">*</span><span class="pl-s1">args</span>, <span class="pl-c1">**</span><span class="pl-s1">kwargs</span>):</td>
        </tr>
        <tr>
          <td id="file-prefixed_mcp_tool-py-L23" class="blob-num js-line-number js-blob-rnum" data-line-number="23"></td>
          <td id="file-prefixed_mcp_tool-py-LC23" class="blob-code blob-code-inner js-file-line">        <span class="pl-s">"""Delegate to the original tool with all arguments</span></td>
        </tr>
        <tr>
          <td id="file-prefixed_mcp_tool-py-L24" class="blob-num js-line-number js-blob-rnum" data-line-number="24"></td>
          <td id="file-prefixed_mcp_tool-py-LC24" class="blob-code blob-code-inner js-file-line"><span class="pl-s">        Important: The *args and **kwargs are crucial here!</span></td>
        </tr>
        <tr>
          <td id="file-prefixed_mcp_tool-py-L25" class="blob-num js-line-number js-blob-rnum" data-line-number="25"></td>
          <td id="file-prefixed_mcp_tool-py-LC25" class="blob-code blob-code-inner js-file-line"><span class="pl-s">        The Strands framework passes additional arguments like </span></td>
        </tr>
        <tr>
          <td id="file-prefixed_mcp_tool-py-L26" class="blob-num js-line-number js-blob-rnum" data-line-number="26"></td>
          <td id="file-prefixed_mcp_tool-py-LC26" class="blob-code blob-code-inner js-file-line"><span class="pl-s">        invocation_state that must be forwarded to the original tool.</span></td>
        </tr>
        <tr>
          <td id="file-prefixed_mcp_tool-py-L27" class="blob-num js-line-number js-blob-rnum" data-line-number="27"></td>
          <td id="file-prefixed_mcp_tool-py-LC27" class="blob-code blob-code-inner js-file-line"><span class="pl-s">        """</span></td>
        </tr>
        <tr>
          <td id="file-prefixed_mcp_tool-py-L28" class="blob-num js-line-number js-blob-rnum" data-line-number="28"></td>
          <td id="file-prefixed_mcp_tool-py-LC28" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">return</span> <span class="pl-s1">self</span>.<span class="pl-c1">_original_tool</span>.<span class="pl-c1">stream</span>(<span class="pl-s1">tool_use</span>, <span class="pl-c1">*</span><span class="pl-s1">args</span>, <span class="pl-c1">**</span><span class="pl-s1">kwargs</span>)</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/8df8b0d2e4ddb734ff2126e3e5f57576/raw/47635481f5835bc2642c55b30471c10fdaccde8e/prefixed_mcp_tool.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/8df8b0d2e4ddb734ff2126e3e5f57576#file-prefixed_mcp_tool-py" class="Link--inTextBlock">
          prefixed_mcp_tool.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>The `PrefixedMCPTool` class does three things:</p><p>1. <strong>Inherits from `AgentTool`</strong>: This is critical. The Strands SDK registry checks `isinstance(tool, AgentTool)`. If your wrapper doesn&#8217;t inherit from `AgentTool`, the registry rejects it. No exceptions.</p><p>2. <strong>Modifies the tool name</strong>: Takes the original tool&#8217;s spec, copies it, and changes just the name. Everything else stays the same - description, parameters, all of it. Just the name gets the prefix.</p><p>3. <strong>Delegates everything else</strong>: All the actual work gets passed to the original tool. The wrapper is just changing the name on the way in.</p><p>The key part is the `stream()` method:</p><pre><code>def stream(self, tool_use, *args, **kwargs):
  return self._original_tool.stream(tool_use, *args, **kwargs)</code></pre><p>That `*args, **kwargs` matters. The Strands framework passes extra arguments to `stream()` - stuff like `invocation_state` that the tool needs. If you don&#8217;t forward those arguments, your tools break at runtime (I learned this the hard way).</p><p>So now instead of registering a tool called `search`, you register a `PrefixedMCPTool` that wraps the original `search` tool but tells the registry its name is `atlassian_search`. The registry sees a unique name. The tool still works exactly the same way.</p><blockquote><p>This ended up working SUPER WELL and I&#8217;m going to do this in ever bot I build going forward (unless the MCP itself already prefixes the platform ahead of the tool name, which they obviously should, but most don&#8217;t today). </p></blockquote><p><strong>## Making It Reusable</strong></p><p>I didn&#8217;t want to wrap every single tool manually, or define this same function in every MCP ingestion script (which are separate files for each MCP due to filtering, authentication, and utilization differences). I needed a single helper function that could take all the tools from an MCP server and prefix them in one shot.</p><p>Here&#8217;s what I built: </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist141861634\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-add_prefix_helper-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;add_prefix_helper.py content, created by KyMidd on 06:09PM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;4\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;add_prefix_helper.py\&quot;>\n        <tr>\n          <td id=\&quot;file-add_prefix_helper-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-add_prefix_helper-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-en>add_prefix_to_mcp_tools</span>(<span class=pl-s1>tools</span>, <span class=pl-s1>prefix</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-add_prefix_helper-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-add_prefix_helper-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>prefix</span> <span class=pl-c1>=</span> <span class=pl-s>f&amp;quot;<span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>prefix</span><span class=pl-kos>}</span></span>_&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-add_prefix_helper-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-add_prefix_helper-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>return</span> [<span class=pl-en>PrefixedMCPTool</span>(<span class=pl-s1>tool</span>, <span class=pl-s1>prefix</span>) <span class=pl-k>for</span> <span class=pl-s1>tool</span> <span class=pl-c1>in</span> <span class=pl-s1>tools</span>]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-add_prefix_helper-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-add_prefix_helper-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c># Get tools from Azure MCP - </span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-add_prefix_helper-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-add_prefix_helper-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s1>azure_mcp_client</span>, <span class=pl-s1>azure_tools</span> <span class=pl-c1>=</span> <span class=pl-en>build_Azure_mcp_client</span>(<span class=pl-s1>azure_client_id</span>, <span class=pl-s1>azure_client_secret</span>, <span class=pl-s1>azure_tenant_id</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-add_prefix_helper-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-add_prefix_helper-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c># Apply prefix - tools like &amp;#39;search&amp;#39; become &amp;#39;azure_search&amp;#39;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-add_prefix_helper-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-add_prefix_helper-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s1>azure_tools</span> <span class=pl-c1>=</span> <span class=pl-en>add_prefix_to_mcp_tools</span>(<span class=pl-s1>azure_tools</span>, <span class=pl-s>&amp;quot;azure&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-add_prefix_helper-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-add_prefix_helper-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c># Add to agent&amp;#39;s tool list</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-add_prefix_helper-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-add_prefix_helper-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s1>tools</span>.<span class=pl-c1>extend</span>(<span class=pl-s1>azure_tools</span>)</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/6e4e89cb308d42bc84b351070fef5cfd/raw/ffcdc186b75b572c06fde10e647ebe0c5c96f330/add_prefix_helper.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/6e4e89cb308d42bc84b351070fef5cfd#file-add_prefix_helper-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          add_prefix_helper.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-b6e2a42a64d4.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-b6e2a42a64d4.css"><div id="gist141861634" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-add_prefix_helper-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="4" data-paste-markdown-skip="" data-tagsearch-path="add_prefix_helper.py">
        <tbody><tr>
          <td id="file-add_prefix_helper-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-add_prefix_helper-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-en">add_prefix_to_mcp_tools</span>(<span class="pl-s1">tools</span>, <span class="pl-s1">prefix</span>):</td>
        </tr>
        <tr>
          <td id="file-add_prefix_helper-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-add_prefix_helper-py-LC2" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">prefix</span> <span class="pl-c1">=</span> <span class="pl-s">f"<span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">prefix</span><span class="pl-kos">}</span></span>_"</span></td>
        </tr>
        <tr>
          <td id="file-add_prefix_helper-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-add_prefix_helper-py-LC3" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">return</span> [<span class="pl-en">PrefixedMCPTool</span>(<span class="pl-s1">tool</span>, <span class="pl-s1">prefix</span>) <span class="pl-k">for</span> <span class="pl-s1">tool</span> <span class="pl-c1">in</span> <span class="pl-s1">tools</span>]</td>
        </tr>
        <tr>
          <td id="file-add_prefix_helper-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-add_prefix_helper-py-LC4" class="blob-code blob-code-inner js-file-line"><span class="pl-c"># Get tools from Azure MCP - </span></td>
        </tr>
        <tr>
          <td id="file-add_prefix_helper-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-add_prefix_helper-py-LC5" class="blob-code blob-code-inner js-file-line"><span class="pl-s1">azure_mcp_client</span>, <span class="pl-s1">azure_tools</span> <span class="pl-c1">=</span> <span class="pl-en">build_Azure_mcp_client</span>(<span class="pl-s1">azure_client_id</span>, <span class="pl-s1">azure_client_secret</span>, <span class="pl-s1">azure_tenant_id</span>)</td>
        </tr>
        <tr>
          <td id="file-add_prefix_helper-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-add_prefix_helper-py-LC6" class="blob-code blob-code-inner js-file-line"><span class="pl-c"># Apply prefix - tools like 'search' become 'azure_search'</span></td>
        </tr>
        <tr>
          <td id="file-add_prefix_helper-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-add_prefix_helper-py-LC7" class="blob-code blob-code-inner js-file-line"><span class="pl-s1">azure_tools</span> <span class="pl-c1">=</span> <span class="pl-en">add_prefix_to_mcp_tools</span>(<span class="pl-s1">azure_tools</span>, <span class="pl-s">"azure"</span>)</td>
        </tr>
        <tr>
          <td id="file-add_prefix_helper-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-add_prefix_helper-py-LC8" class="blob-code blob-code-inner js-file-line"><span class="pl-c"># Add to agent's tool list</span></td>
        </tr>
        <tr>
          <td id="file-add_prefix_helper-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-add_prefix_helper-py-LC9" class="blob-code blob-code-inner js-file-line"><span class="pl-s1">tools</span>.<span class="pl-c1">extend</span>(<span class="pl-s1">azure_tools</span>)</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/6e4e89cb308d42bc84b351070fef5cfd/raw/ffcdc186b75b572c06fde10e647ebe0c5c96f330/add_prefix_helper.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/6e4e89cb308d42bc84b351070fef5cfd#file-add_prefix_helper-py" class="Link--inTextBlock">
          add_prefix_helper.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>The `add_prefix_to_mcp_tools()` function takes two arguments:</p><p>- `tools`: The list of tools from your MCP server</p><p>- `prefix`: The prefix you want to add (like &#8220;github&#8221; or &#8220;azure&#8221;)</p><p>It automatically adds the underscore, so you just pass `&#8221;github&#8221;` and it creates `github_search`, `github_get_issue`, etc.</p><p>Do this for each MCP server with a different prefix:</p><pre><code>atlassian_tools = add_prefix_to_mcp_tools(atlassian_tools, &#8220;atlassian&#8221;)
github_tools = add_prefix_to_mcp_tools(azure_tools, &#8220;github&#8221;)
pagerduty_tools = add_prefix_to_mcp_tools(pagerduty_tools, &#8220;pagerduty&#8221;)</code></pre><p>Before prefixing, I had:</p><p>- Atlassian: `search`, `fetch`</p><p>- Azure: `search`</p><p>After prefixing:</p><p>- Atlassian: `atlassian_search`, `atlassian_fetch`</p><p>- Azure: `azure_search`</p><p>- No collision, bot works</p><p>The agent now has access to 106 tools total (25 from GitHub, 19 from Atlassian, 15 from PagerDuty, 47 from Azure), and every single one has a unique name, and all are accessible at runtime. </p><p>With my luck, AWS (my next target) will also have a &#8220;search&#8221; tool, but now I won&#8217;t really care, we won&#8217;t see the collision even if it does. </p><h1><strong>Summary</strong></h1><p>If you&#8217;re using multiple MCP servers with your AI agents, you&#8217;ll hit tool name collisions eventually. It&#8217;d be amazing if some registry came out that helped folks establish a standard naming scheme, validated partners and sources, much like the GitHub Marketplace, or Azure Marketplace, Hashi Marketplace etc., but it doesn&#8217;t exist yet. </p><p>I&#8217;m excited to no longer care what tools we register - now I can just add MCPs and their tools, and go back to worrying about AIs deleting my environment, or taking over the world. </p><p>Thanks ya&#8217;ll. Good luck out there. <br>kyler</p>]]></content:encoded></item><item><title><![CDATA[🔥Solving AWS Bedrock's Enterprise Logging Problem: Adding Bot Context to Model Invocation Logs🔥 ]]></title><description><![CDATA[aka, who is using up all our AI tokens?]]></description><link>https://www.letsdodevops.com/p/solving-aws-bedrocks-enterprise-logging</link><guid isPermaLink="false">https://www.letsdodevops.com/p/solving-aws-bedrocks-enterprise-logging</guid><dc:creator><![CDATA[Kyler Middleton]]></dc:creator><pubDate>Tue, 14 Oct 2025 14:00:49 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!WSfC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F69902d70-016b-48bc-a146-a9af32bb9b36_1292x1278.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote><p><em>This blog series focuses on presenting complex DevOps projects as simple and approachable via plain language and lots of pictures. You can do it!</em></p><p>These articles are supported by readers, <a href="https://www.letsdodevops.com/subscribe">please consider subscribing</a> to support me writing more of these articles &lt;3 :)</p></blockquote><p>Hey all! Today we&#8217;re tackling a problem that&#8217;s been driving me crazy since we started scaling our AWS Bedrock bot deployments across the enterprise. You&#8217;d think that when you have multiple AI bots running in production, AWS would give you some reasonable way to separate and monitor their activities. You&#8217;d be wrong.</p><p>Here&#8217;s the issue: every single AWS Bedrock model invocation in a region gets dumped into one massive CloudWatch log group. Doesn&#8217;t matter if you have five different bots serving different teams, or if Bot A is handling customer support while Bot B is doing internal documentation queries. Everything goes into `/aws/bedrock/modelinvocations` and good luck figuring out which log entry came from which bot.</p><p>This creates a monitoring nightmare. When someone asks &#8220;how much is our Slack bot costing us?&#8221; or &#8220;why did the support bot give a weird response yesterday?&#8221; you&#8217;re stuck grep-ing through thousands of log entries that all look identical. The raw Bedrock logs contain the model inputs and outputs, but zero context about which application made the call or who the actual user was.</p><p>After dealing with this mess for way too long, I finally built a solution that transforms these useless raw logs into something actually meaningful for enterprise monitoring. We&#8217;re talking about a Kinesis Firehose pipeline with a custom Lambda processor that parses the Bedrock JSON and adds the context that should have been there in the first place.</p><p>The enhancement adds four key fields that make all the difference: `botName` (extracted from the IAM role), `botOutput` (what the bot actually said to the user), `user` (who asked the question), and `query` (what they asked). Suddenly, instead of cryptic JSON blobs, you have logs that tell a story about real conversations happening in your enterprise.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!WSfC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F69902d70-016b-48bc-a146-a9af32bb9b36_1292x1278.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!WSfC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F69902d70-016b-48bc-a146-a9af32bb9b36_1292x1278.png 424w, https://substackcdn.com/image/fetch/$s_!WSfC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F69902d70-016b-48bc-a146-a9af32bb9b36_1292x1278.png 848w, https://substackcdn.com/image/fetch/$s_!WSfC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F69902d70-016b-48bc-a146-a9af32bb9b36_1292x1278.png 1272w, https://substackcdn.com/image/fetch/$s_!WSfC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F69902d70-016b-48bc-a146-a9af32bb9b36_1292x1278.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!WSfC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F69902d70-016b-48bc-a146-a9af32bb9b36_1292x1278.png" width="1292" height="1278" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/69902d70-016b-48bc-a146-a9af32bb9b36_1292x1278.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1278,&quot;width&quot;:1292,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:167809,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.letsdodevops.com/i/174565972?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F69902d70-016b-48bc-a146-a9af32bb9b36_1292x1278.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!WSfC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F69902d70-016b-48bc-a146-a9af32bb9b36_1292x1278.png 424w, https://substackcdn.com/image/fetch/$s_!WSfC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F69902d70-016b-48bc-a146-a9af32bb9b36_1292x1278.png 848w, https://substackcdn.com/image/fetch/$s_!WSfC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F69902d70-016b-48bc-a146-a9af32bb9b36_1292x1278.png 1272w, https://substackcdn.com/image/fetch/$s_!WSfC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F69902d70-016b-48bc-a146-a9af32bb9b36_1292x1278.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This approach works for any AWS Bedrock deployment, but it&#8217;s especially valuable if you&#8217;re running multiple bots or need to track usage by team, project, or individual users. The processed logs flow into whatever monitoring system you&#8217;re already using - in our case, Splunk - where they become the foundation for dashboards, alerting, and cost attribution.</p><p>Let&#8217;s walk through how this whole system works and why it&#8217;s become essential for managing AI deployments at enterprise scale.</p><p>If you don&#8217;t care about the write-up and just want the code, you can find the modified cloudwatch to splunk lambda that adds bot information here: </p><blockquote><p><a href="https://gist.github.com/KyMidd/f2d216682b0c40ca06d74742f1cc56f7">gist.github.com/KyMidd/f2d216682b0c40ca06d74742f1cc56f7</a></p></blockquote><p></p><h1><strong>The Problem: AWS Bedrock&#8217;s Logging Black Hole</strong></h1><p>To understand why this solution matters, you need to see just how broken the default AWS Bedrock logging situation really is. When you make a model invocation through Bedrock, AWS dutifully logs the entire interaction to CloudWatch - which sounds great until you realize where those logs actually end up.</p><p>Every single model invocation in a region gets dumped into `/aws/bedrock/modelinvocations`. Your customer support bot? Same log group. Your internal documentation bot? Same log group. That experimental bot someone in marketing is testing? Yep, same log group. It&#8217;s like throwing all your mail into one giant pile and then wondering why you can&#8217;t find anything.</p><p>AWS does include some metadata with each log entry - most importantly, the IAM role ARN that made the request. So technically, you can tell which bot generated which log entry. But here&#8217;s the catch: you have to parse through thousands of nearly identical JSON blobs to find the entries you care about, and even then, the raw logs don&#8217;t tell you anything useful about the actual conversation.</p><p>Here&#8217;s what a typical raw Bedrock log entry looks like - it&#8217;s hundreds of lines of nested JSON containing the full model request and response, but buried so deep in structure that extracting meaningful information requires serious JSON archaeology. You can see the complete input and output, but good luck figuring out which user asked what question or what the bot actually said back to them.</p><p>The enterprise implications are brutal. When your CFO asks &#8220;how much are we spending on the Slack bot versus the customer support bot?&#8221; you can&#8217;t answer without building custom tooling to parse through the log mess. When a user reports &#8220;the bot gave me a weird answer yesterday,&#8221; you&#8217;re looking at potentially thousands of log entries with no easy way to find their specific interaction.</p><p>Even basic monitoring becomes a nightmare. Want to set up alerts for when a specific bot starts giving errors? You&#8217;ll need to craft complex CloudWatch filters that parse the IAM role ARN and hope you can distinguish between different types of failures. Want to track usage patterns or popular queries? Hope you enjoy writing custom log analysis scripts.</p><p>The root issue is that AWS designed Bedrock logging for debugging individual model calls, not for managing multiple production AI applications. They give you all the technical details about the model invocation but none of the business context about who asked what and why. For a single-bot deployment, this might be manageable. For enterprise deployments with multiple bots serving different teams and use cases, it&#8217;s completely unworkable.</p><h1><strong>The Solution: Enhanced Log Processing Pipeline</strong></h1><p>The answer to this mess is surprisingly straightforward: transform them on their way to your monitoring system.</p><p>Here&#8217;s the architecture: CloudWatch subscription filter &#8594; Kinesis Firehose &#8594; Lambda processor &#8594; Splunk. The raw Bedrock logs stay exactly as AWS created them in CloudWatch. But when they flow to Splunk through our pipeline, they get enhanced with the context that actually matters.</p><p>The Lambda processor is where the magic happens. It takes those massive, cryptic JSON blobs and transforms them into something humans can actually understand. Instead of hundreds of lines of nested structures arriving in Splunk, you get clean log entries with the four fields that matter: `botName` (which bot generated this), `botOutput` (what it actually said), `user` (who asked the question), and `query` (what they asked).</p><p>The processor extracts the bot name from the IAM role ARN, navigates through the complex nested JSON to find the actual response text, and parses conversation formats to identify users and their questions. What used to require custom scripts and manual investigation now happens automatically for every single log entry flowing into your monitoring system.</p><p>The beauty of this approach is that it&#8217;s completely non-intrusive. Your CloudWatch logs remain untouched for AWS debugging purposes, but your enterprise monitoring gets the enriched context it needs. The whole pipeline uses standard AWS services, scales automatically, and integrates seamlessly with whatever log aggregation platform you&#8217;re already using.</p><h1><strong>Log Enhancement</strong></h1><p>The real work happens inside the Lambda processor, where we parse the Bedrock JSON and extract the four fields that actually matter for enterprise monitoring.</p><p><strong>Bot Name Extraction</strong></p><p>This one&#8217;s straightforward. Every Bedrock log includes the IAM role ARN that made the request. We just grab the last segment after the final slash - so `arn:aws:iam::123456789012:role/VeraSlackBot-Worker` becomes `VeraSlackBot-Worker`. Simple, but effective for distinguishing between different bots.</p><p><strong>Bot Output Parsing</strong></p><p>This is where things get messy. The actual response text is buried deep in the JSON structure at `output.outputBodyJson.output.message.content[0].text`. That&#8217;s five levels of nesting just to find out what the bot said to the user.</p><p><strong>User and Query Extraction</strong></p><p>The most complex part involves parsing the conversation format from the input messages. Our bots format user messages as &#8220;Kyler (she/her) says: Now a dog.&#8221; - so we need to split on &#8220; says: &#8220; and extract the username before any parentheses. The query is everything after the colon.</p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist141198389\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-bedrock_log_enhancement-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;bedrock_log_enhancement.py content, created by KyMidd on 08:41PM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;4\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;bedrock_log_enhancement.py\&quot;>\n        <tr>\n          <td id=\&quot;file-bedrock_log_enhancement-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-bedrock_log_enhancement-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-en>transformLogEvent</span>(<span class=pl-s1>log_event</span>, <span class=pl-s1>acct</span>, <span class=pl-s1>arn</span>, <span class=pl-s1>loggrp</span>, <span class=pl-s1>logstrm</span>, <span class=pl-s1>filterName</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_log_enhancement-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-bedrock_log_enhancement-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>enhanced_event</span> <span class=pl-c1>=</span> {}</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_log_enhancement-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-bedrock_log_enhancement-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>try</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_log_enhancement-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-bedrock_log_enhancement-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>enhanced_event</span> <span class=pl-c1>=</span> <span class=pl-s1>json</span>.<span class=pl-c1>loads</span>(<span class=pl-s1>log_event</span>[<span class=pl-s>&amp;#39;message&amp;#39;</span>])</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_log_enhancement-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-bedrock_log_enhancement-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>except</span> <span class=pl-s1>json</span>.<span class=pl-c1>JSONDecodeError</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_log_enhancement-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-bedrock_log_enhancement-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>enhanced_event</span> <span class=pl-c1>=</span> {<span class=pl-s>&amp;quot;raw_message&amp;quot;</span>: <span class=pl-s1>log_event</span>[<span class=pl-s>&amp;#39;message&amp;#39;</span>]}</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_log_enhancement-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-bedrock_log_enhancement-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Extract botOutput from output.outputBodyJson.output.message.content[0].text</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_log_enhancement-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-bedrock_log_enhancement-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>try</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_log_enhancement-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-bedrock_log_enhancement-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>if</span> (<span class=pl-s>&amp;#39;output&amp;#39;</span> <span class=pl-c1>in</span> <span class=pl-s1>enhanced_event</span> <span class=pl-c1>and</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_log_enhancement-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-bedrock_log_enhancement-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s>&amp;#39;outputBodyJson&amp;#39;</span> <span class=pl-c1>in</span> <span class=pl-s1>enhanced_event</span>[<span class=pl-s>&amp;#39;output&amp;#39;</span>] <span class=pl-c1>and</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_log_enhancement-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-bedrock_log_enhancement-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s>&amp;#39;output&amp;#39;</span> <span class=pl-c1>in</span> <span class=pl-s1>enhanced_event</span>[<span class=pl-s>&amp;#39;output&amp;#39;</span>][<span class=pl-s>&amp;#39;outputBodyJson&amp;#39;</span>] <span class=pl-c1>and</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_log_enhancement-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-bedrock_log_enhancement-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s>&amp;#39;message&amp;#39;</span> <span class=pl-c1>in</span> <span class=pl-s1>enhanced_event</span>[<span class=pl-s>&amp;#39;output&amp;#39;</span>][<span class=pl-s>&amp;#39;outputBodyJson&amp;#39;</span>][<span class=pl-s>&amp;#39;output&amp;#39;</span>] <span class=pl-c1>and</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_log_enhancement-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-bedrock_log_enhancement-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s>&amp;#39;content&amp;#39;</span> <span class=pl-c1>in</span> <span class=pl-s1>enhanced_event</span>[<span class=pl-s>&amp;#39;output&amp;#39;</span>][<span class=pl-s>&amp;#39;outputBodyJson&amp;#39;</span>][<span class=pl-s>&amp;#39;output&amp;#39;</span>][<span class=pl-s>&amp;#39;message&amp;#39;</span>]):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_log_enhancement-py-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-bedrock_log_enhancement-py-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_log_enhancement-py-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-bedrock_log_enhancement-py-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>content_array</span> <span class=pl-c1>=</span> <span class=pl-s1>enhanced_event</span>[<span class=pl-s>&amp;#39;output&amp;#39;</span>][<span class=pl-s>&amp;#39;outputBodyJson&amp;#39;</span>][<span class=pl-s>&amp;#39;output&amp;#39;</span>][<span class=pl-s>&amp;#39;message&amp;#39;</span>][<span class=pl-s>&amp;#39;content&amp;#39;</span>]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_log_enhancement-py-L16\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;16\&quot;></td>\n          <td id=\&quot;file-bedrock_log_enhancement-py-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-k>if</span> <span class=pl-en>len</span>(<span class=pl-s1>content_array</span>) <span class=pl-c1>&amp;gt;</span> <span class=pl-c1>0</span> <span class=pl-c1>and</span> <span class=pl-s>&amp;#39;text&amp;#39;</span> <span class=pl-c1>in</span> <span class=pl-s1>content_array</span>[<span class=pl-c1>0</span>]:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_log_enhancement-py-L17\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;17\&quot;></td>\n          <td id=\&quot;file-bedrock_log_enhancement-py-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                <span class=pl-s1>enhanced_event</span>[<span class=pl-s>&amp;#39;botOutput&amp;#39;</span>] <span class=pl-c1>=</span> <span class=pl-s1>content_array</span>[<span class=pl-c1>0</span>][<span class=pl-s>&amp;#39;text&amp;#39;</span>]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_log_enhancement-py-L18\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;18\&quot;></td>\n          <td id=\&quot;file-bedrock_log_enhancement-py-LC18\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>except</span> (<span class=pl-v>KeyError</span>, <span class=pl-v>TypeError</span>, <span class=pl-v>IndexError</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_log_enhancement-py-L19\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;19\&quot;></td>\n          <td id=\&quot;file-bedrock_log_enhancement-py-LC19\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>pass</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_log_enhancement-py-L20\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;20\&quot;></td>\n          <td id=\&quot;file-bedrock_log_enhancement-py-LC20\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Extract user and query from input.inputBodyJson.messages</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_log_enhancement-py-L21\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;21\&quot;></td>\n          <td id=\&quot;file-bedrock_log_enhancement-py-LC21\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>try</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_log_enhancement-py-L22\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;22\&quot;></td>\n          <td id=\&quot;file-bedrock_log_enhancement-py-LC22\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>if</span> (<span class=pl-s>&amp;#39;input&amp;#39;</span> <span class=pl-c1>in</span> <span class=pl-s1>enhanced_event</span> <span class=pl-c1>and</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_log_enhancement-py-L23\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;23\&quot;></td>\n          <td id=\&quot;file-bedrock_log_enhancement-py-LC23\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s>&amp;#39;inputBodyJson&amp;#39;</span> <span class=pl-c1>in</span> <span class=pl-s1>enhanced_event</span>[<span class=pl-s>&amp;#39;input&amp;#39;</span>] <span class=pl-c1>and</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_log_enhancement-py-L24\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;24\&quot;></td>\n          <td id=\&quot;file-bedrock_log_enhancement-py-LC24\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s>&amp;#39;messages&amp;#39;</span> <span class=pl-c1>in</span> <span class=pl-s1>enhanced_event</span>[<span class=pl-s>&amp;#39;input&amp;#39;</span>][<span class=pl-s>&amp;#39;inputBodyJson&amp;#39;</span>]):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_log_enhancement-py-L25\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;25\&quot;></td>\n          <td id=\&quot;file-bedrock_log_enhancement-py-LC25\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_log_enhancement-py-L26\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;26\&quot;></td>\n          <td id=\&quot;file-bedrock_log_enhancement-py-LC26\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>messages</span> <span class=pl-c1>=</span> <span class=pl-s1>enhanced_event</span>[<span class=pl-s>&amp;#39;input&amp;#39;</span>][<span class=pl-s>&amp;#39;inputBodyJson&amp;#39;</span>][<span class=pl-s>&amp;#39;messages&amp;#39;</span>]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_log_enhancement-py-L27\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;27\&quot;></td>\n          <td id=\&quot;file-bedrock_log_enhancement-py-LC27\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-k>for</span> <span class=pl-s1>message</span> <span class=pl-c1>in</span> <span class=pl-en>reversed</span>(<span class=pl-s1>messages</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_log_enhancement-py-L28\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;28\&quot;></td>\n          <td id=\&quot;file-bedrock_log_enhancement-py-LC28\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                <span class=pl-k>if</span> (<span class=pl-s1>message</span>.<span class=pl-c1>get</span>(<span class=pl-s>&amp;#39;role&amp;#39;</span>) <span class=pl-c1>==</span> <span class=pl-s>&amp;#39;user&amp;#39;</span> <span class=pl-c1>and</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_log_enhancement-py-L29\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;29\&quot;></td>\n          <td id=\&quot;file-bedrock_log_enhancement-py-LC29\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                    <span class=pl-s>&amp;#39;content&amp;#39;</span> <span class=pl-c1>in</span> <span class=pl-s1>message</span> <span class=pl-c1>and</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_log_enhancement-py-L30\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;30\&quot;></td>\n          <td id=\&quot;file-bedrock_log_enhancement-py-LC30\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                    <span class=pl-en>len</span>(<span class=pl-s1>message</span>[<span class=pl-s>&amp;#39;content&amp;#39;</span>]) <span class=pl-c1>&amp;gt;</span> <span class=pl-c1>0</span> <span class=pl-c1>and</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_log_enhancement-py-L31\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;31\&quot;></td>\n          <td id=\&quot;file-bedrock_log_enhancement-py-LC31\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                    <span class=pl-s>&amp;#39;text&amp;#39;</span> <span class=pl-c1>in</span> <span class=pl-s1>message</span>[<span class=pl-s>&amp;#39;content&amp;#39;</span>][<span class=pl-c1>0</span>]):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_log_enhancement-py-L32\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;32\&quot;></td>\n          <td id=\&quot;file-bedrock_log_enhancement-py-LC32\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                    </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_log_enhancement-py-L33\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;33\&quot;></td>\n          <td id=\&quot;file-bedrock_log_enhancement-py-LC33\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                    <span class=pl-s1>user_text</span> <span class=pl-c1>=</span> <span class=pl-s1>message</span>[<span class=pl-s>&amp;#39;content&amp;#39;</span>][<span class=pl-c1>0</span>][<span class=pl-s>&amp;#39;text&amp;#39;</span>]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_log_enhancement-py-L34\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;34\&quot;></td>\n          <td id=\&quot;file-bedrock_log_enhancement-py-LC34\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                    <span class=pl-k>if</span> <span class=pl-s>&amp;#39; says: &amp;#39;</span> <span class=pl-c1>in</span> <span class=pl-s1>user_text</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_log_enhancement-py-L35\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;35\&quot;></td>\n          <td id=\&quot;file-bedrock_log_enhancement-py-LC35\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                        <span class=pl-s1>user_part</span>, <span class=pl-s1>query_part</span> <span class=pl-c1>=</span> <span class=pl-s1>user_text</span>.<span class=pl-c1>split</span>(<span class=pl-s>&amp;#39; says: &amp;#39;</span>, <span class=pl-c1>1</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_log_enhancement-py-L36\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;36\&quot;></td>\n          <td id=\&quot;file-bedrock_log_enhancement-py-LC36\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                        <span class=pl-s1>enhanced_event</span>[<span class=pl-s>&amp;#39;user&amp;#39;</span>] <span class=pl-c1>=</span> <span class=pl-s1>user_part</span>.<span class=pl-c1>split</span>(<span class=pl-s>&amp;#39; (&amp;#39;</span>)[<span class=pl-c1>0</span>]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_log_enhancement-py-L37\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;37\&quot;></td>\n          <td id=\&quot;file-bedrock_log_enhancement-py-LC37\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                        <span class=pl-s1>enhanced_event</span>[<span class=pl-s>&amp;#39;query&amp;#39;</span>] <span class=pl-c1>=</span> <span class=pl-s1>query_part</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_log_enhancement-py-L38\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;38\&quot;></td>\n          <td id=\&quot;file-bedrock_log_enhancement-py-LC38\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                    <span class=pl-k>break</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_log_enhancement-py-L39\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;39\&quot;></td>\n          <td id=\&quot;file-bedrock_log_enhancement-py-LC39\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>except</span> (<span class=pl-v>KeyError</span>, <span class=pl-v>TypeError</span>, <span class=pl-v>IndexError</span>, <span class=pl-v>ValueError</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_log_enhancement-py-L40\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;40\&quot;></td>\n          <td id=\&quot;file-bedrock_log_enhancement-py-LC40\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>pass</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_log_enhancement-py-L41\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;41\&quot;></td>\n          <td id=\&quot;file-bedrock_log_enhancement-py-LC41\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>return</span> <span class=pl-s1>json</span>.<span class=pl-c1>dumps</span>(<span class=pl-s1>enhanced_event</span>)</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/e53599aed495806b61d03faa272f339a/raw/009b2586c7879bf003e27b33235bc9b2bef2954d/bedrock_log_enhancement.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/e53599aed495806b61d03faa272f339a#file-bedrock_log_enhancement-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          bedrock_log_enhancement.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-59543e005c9c.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-59543e005c9c.css"><div id="gist141198389" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-bedrock_log_enhancement-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="4" data-paste-markdown-skip="" data-tagsearch-path="bedrock_log_enhancement.py">
        <tbody><tr>
          <td id="file-bedrock_log_enhancement-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-bedrock_log_enhancement-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-en">transformLogEvent</span>(<span class="pl-s1">log_event</span>, <span class="pl-s1">acct</span>, <span class="pl-s1">arn</span>, <span class="pl-s1">loggrp</span>, <span class="pl-s1">logstrm</span>, <span class="pl-s1">filterName</span>):</td>
        </tr>
        <tr>
          <td id="file-bedrock_log_enhancement-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-bedrock_log_enhancement-py-LC2" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">enhanced_event</span> <span class="pl-c1">=</span> {}</td>
        </tr>
        <tr>
          <td id="file-bedrock_log_enhancement-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-bedrock_log_enhancement-py-LC3" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">try</span>:</td>
        </tr>
        <tr>
          <td id="file-bedrock_log_enhancement-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-bedrock_log_enhancement-py-LC4" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">enhanced_event</span> <span class="pl-c1">=</span> <span class="pl-s1">json</span>.<span class="pl-c1">loads</span>(<span class="pl-s1">log_event</span>[<span class="pl-s">'message'</span>])</td>
        </tr>
        <tr>
          <td id="file-bedrock_log_enhancement-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-bedrock_log_enhancement-py-LC5" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">except</span> <span class="pl-s1">json</span>.<span class="pl-c1">JSONDecodeError</span>:</td>
        </tr>
        <tr>
          <td id="file-bedrock_log_enhancement-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-bedrock_log_enhancement-py-LC6" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">enhanced_event</span> <span class="pl-c1">=</span> {<span class="pl-s">"raw_message"</span>: <span class="pl-s1">log_event</span>[<span class="pl-s">'message'</span>]}</td>
        </tr>
        <tr>
          <td id="file-bedrock_log_enhancement-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-bedrock_log_enhancement-py-LC7" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Extract botOutput from output.outputBodyJson.output.message.content[0].text</span></td>
        </tr>
        <tr>
          <td id="file-bedrock_log_enhancement-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-bedrock_log_enhancement-py-LC8" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">try</span>:</td>
        </tr>
        <tr>
          <td id="file-bedrock_log_enhancement-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-bedrock_log_enhancement-py-LC9" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">if</span> (<span class="pl-s">'output'</span> <span class="pl-c1">in</span> <span class="pl-s1">enhanced_event</span> <span class="pl-c1">and</span></td>
        </tr>
        <tr>
          <td id="file-bedrock_log_enhancement-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-bedrock_log_enhancement-py-LC10" class="blob-code blob-code-inner js-file-line">            <span class="pl-s">'outputBodyJson'</span> <span class="pl-c1">in</span> <span class="pl-s1">enhanced_event</span>[<span class="pl-s">'output'</span>] <span class="pl-c1">and</span></td>
        </tr>
        <tr>
          <td id="file-bedrock_log_enhancement-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-bedrock_log_enhancement-py-LC11" class="blob-code blob-code-inner js-file-line">            <span class="pl-s">'output'</span> <span class="pl-c1">in</span> <span class="pl-s1">enhanced_event</span>[<span class="pl-s">'output'</span>][<span class="pl-s">'outputBodyJson'</span>] <span class="pl-c1">and</span></td>
        </tr>
        <tr>
          <td id="file-bedrock_log_enhancement-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-bedrock_log_enhancement-py-LC12" class="blob-code blob-code-inner js-file-line">            <span class="pl-s">'message'</span> <span class="pl-c1">in</span> <span class="pl-s1">enhanced_event</span>[<span class="pl-s">'output'</span>][<span class="pl-s">'outputBodyJson'</span>][<span class="pl-s">'output'</span>] <span class="pl-c1">and</span></td>
        </tr>
        <tr>
          <td id="file-bedrock_log_enhancement-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-bedrock_log_enhancement-py-LC13" class="blob-code blob-code-inner js-file-line">            <span class="pl-s">'content'</span> <span class="pl-c1">in</span> <span class="pl-s1">enhanced_event</span>[<span class="pl-s">'output'</span>][<span class="pl-s">'outputBodyJson'</span>][<span class="pl-s">'output'</span>][<span class="pl-s">'message'</span>]):</td>
        </tr>
        <tr>
          <td id="file-bedrock_log_enhancement-py-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-bedrock_log_enhancement-py-LC14" class="blob-code blob-code-inner js-file-line">            </td>
        </tr>
        <tr>
          <td id="file-bedrock_log_enhancement-py-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-bedrock_log_enhancement-py-LC15" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">content_array</span> <span class="pl-c1">=</span> <span class="pl-s1">enhanced_event</span>[<span class="pl-s">'output'</span>][<span class="pl-s">'outputBodyJson'</span>][<span class="pl-s">'output'</span>][<span class="pl-s">'message'</span>][<span class="pl-s">'content'</span>]</td>
        </tr>
        <tr>
          <td id="file-bedrock_log_enhancement-py-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-bedrock_log_enhancement-py-LC16" class="blob-code blob-code-inner js-file-line">            <span class="pl-k">if</span> <span class="pl-en">len</span>(<span class="pl-s1">content_array</span>) <span class="pl-c1">&gt;</span> <span class="pl-c1">0</span> <span class="pl-c1">and</span> <span class="pl-s">'text'</span> <span class="pl-c1">in</span> <span class="pl-s1">content_array</span>[<span class="pl-c1">0</span>]:</td>
        </tr>
        <tr>
          <td id="file-bedrock_log_enhancement-py-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-bedrock_log_enhancement-py-LC17" class="blob-code blob-code-inner js-file-line">                <span class="pl-s1">enhanced_event</span>[<span class="pl-s">'botOutput'</span>] <span class="pl-c1">=</span> <span class="pl-s1">content_array</span>[<span class="pl-c1">0</span>][<span class="pl-s">'text'</span>]</td>
        </tr>
        <tr>
          <td id="file-bedrock_log_enhancement-py-L18" class="blob-num js-line-number js-blob-rnum" data-line-number="18"></td>
          <td id="file-bedrock_log_enhancement-py-LC18" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">except</span> (<span class="pl-v">KeyError</span>, <span class="pl-v">TypeError</span>, <span class="pl-v">IndexError</span>):</td>
        </tr>
        <tr>
          <td id="file-bedrock_log_enhancement-py-L19" class="blob-num js-line-number js-blob-rnum" data-line-number="19"></td>
          <td id="file-bedrock_log_enhancement-py-LC19" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">pass</span></td>
        </tr>
        <tr>
          <td id="file-bedrock_log_enhancement-py-L20" class="blob-num js-line-number js-blob-rnum" data-line-number="20"></td>
          <td id="file-bedrock_log_enhancement-py-LC20" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Extract user and query from input.inputBodyJson.messages</span></td>
        </tr>
        <tr>
          <td id="file-bedrock_log_enhancement-py-L21" class="blob-num js-line-number js-blob-rnum" data-line-number="21"></td>
          <td id="file-bedrock_log_enhancement-py-LC21" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">try</span>:</td>
        </tr>
        <tr>
          <td id="file-bedrock_log_enhancement-py-L22" class="blob-num js-line-number js-blob-rnum" data-line-number="22"></td>
          <td id="file-bedrock_log_enhancement-py-LC22" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">if</span> (<span class="pl-s">'input'</span> <span class="pl-c1">in</span> <span class="pl-s1">enhanced_event</span> <span class="pl-c1">and</span></td>
        </tr>
        <tr>
          <td id="file-bedrock_log_enhancement-py-L23" class="blob-num js-line-number js-blob-rnum" data-line-number="23"></td>
          <td id="file-bedrock_log_enhancement-py-LC23" class="blob-code blob-code-inner js-file-line">            <span class="pl-s">'inputBodyJson'</span> <span class="pl-c1">in</span> <span class="pl-s1">enhanced_event</span>[<span class="pl-s">'input'</span>] <span class="pl-c1">and</span></td>
        </tr>
        <tr>
          <td id="file-bedrock_log_enhancement-py-L24" class="blob-num js-line-number js-blob-rnum" data-line-number="24"></td>
          <td id="file-bedrock_log_enhancement-py-LC24" class="blob-code blob-code-inner js-file-line">            <span class="pl-s">'messages'</span> <span class="pl-c1">in</span> <span class="pl-s1">enhanced_event</span>[<span class="pl-s">'input'</span>][<span class="pl-s">'inputBodyJson'</span>]):</td>
        </tr>
        <tr>
          <td id="file-bedrock_log_enhancement-py-L25" class="blob-num js-line-number js-blob-rnum" data-line-number="25"></td>
          <td id="file-bedrock_log_enhancement-py-LC25" class="blob-code blob-code-inner js-file-line">            </td>
        </tr>
        <tr>
          <td id="file-bedrock_log_enhancement-py-L26" class="blob-num js-line-number js-blob-rnum" data-line-number="26"></td>
          <td id="file-bedrock_log_enhancement-py-LC26" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">messages</span> <span class="pl-c1">=</span> <span class="pl-s1">enhanced_event</span>[<span class="pl-s">'input'</span>][<span class="pl-s">'inputBodyJson'</span>][<span class="pl-s">'messages'</span>]</td>
        </tr>
        <tr>
          <td id="file-bedrock_log_enhancement-py-L27" class="blob-num js-line-number js-blob-rnum" data-line-number="27"></td>
          <td id="file-bedrock_log_enhancement-py-LC27" class="blob-code blob-code-inner js-file-line">            <span class="pl-k">for</span> <span class="pl-s1">message</span> <span class="pl-c1">in</span> <span class="pl-en">reversed</span>(<span class="pl-s1">messages</span>):</td>
        </tr>
        <tr>
          <td id="file-bedrock_log_enhancement-py-L28" class="blob-num js-line-number js-blob-rnum" data-line-number="28"></td>
          <td id="file-bedrock_log_enhancement-py-LC28" class="blob-code blob-code-inner js-file-line">                <span class="pl-k">if</span> (<span class="pl-s1">message</span>.<span class="pl-c1">get</span>(<span class="pl-s">'role'</span>) <span class="pl-c1">==</span> <span class="pl-s">'user'</span> <span class="pl-c1">and</span></td>
        </tr>
        <tr>
          <td id="file-bedrock_log_enhancement-py-L29" class="blob-num js-line-number js-blob-rnum" data-line-number="29"></td>
          <td id="file-bedrock_log_enhancement-py-LC29" class="blob-code blob-code-inner js-file-line">                    <span class="pl-s">'content'</span> <span class="pl-c1">in</span> <span class="pl-s1">message</span> <span class="pl-c1">and</span></td>
        </tr>
        <tr>
          <td id="file-bedrock_log_enhancement-py-L30" class="blob-num js-line-number js-blob-rnum" data-line-number="30"></td>
          <td id="file-bedrock_log_enhancement-py-LC30" class="blob-code blob-code-inner js-file-line">                    <span class="pl-en">len</span>(<span class="pl-s1">message</span>[<span class="pl-s">'content'</span>]) <span class="pl-c1">&gt;</span> <span class="pl-c1">0</span> <span class="pl-c1">and</span></td>
        </tr>
        <tr>
          <td id="file-bedrock_log_enhancement-py-L31" class="blob-num js-line-number js-blob-rnum" data-line-number="31"></td>
          <td id="file-bedrock_log_enhancement-py-LC31" class="blob-code blob-code-inner js-file-line">                    <span class="pl-s">'text'</span> <span class="pl-c1">in</span> <span class="pl-s1">message</span>[<span class="pl-s">'content'</span>][<span class="pl-c1">0</span>]):</td>
        </tr>
        <tr>
          <td id="file-bedrock_log_enhancement-py-L32" class="blob-num js-line-number js-blob-rnum" data-line-number="32"></td>
          <td id="file-bedrock_log_enhancement-py-LC32" class="blob-code blob-code-inner js-file-line">                    </td>
        </tr>
        <tr>
          <td id="file-bedrock_log_enhancement-py-L33" class="blob-num js-line-number js-blob-rnum" data-line-number="33"></td>
          <td id="file-bedrock_log_enhancement-py-LC33" class="blob-code blob-code-inner js-file-line">                    <span class="pl-s1">user_text</span> <span class="pl-c1">=</span> <span class="pl-s1">message</span>[<span class="pl-s">'content'</span>][<span class="pl-c1">0</span>][<span class="pl-s">'text'</span>]</td>
        </tr>
        <tr>
          <td id="file-bedrock_log_enhancement-py-L34" class="blob-num js-line-number js-blob-rnum" data-line-number="34"></td>
          <td id="file-bedrock_log_enhancement-py-LC34" class="blob-code blob-code-inner js-file-line">                    <span class="pl-k">if</span> <span class="pl-s">' says: '</span> <span class="pl-c1">in</span> <span class="pl-s1">user_text</span>:</td>
        </tr>
        <tr>
          <td id="file-bedrock_log_enhancement-py-L35" class="blob-num js-line-number js-blob-rnum" data-line-number="35"></td>
          <td id="file-bedrock_log_enhancement-py-LC35" class="blob-code blob-code-inner js-file-line">                        <span class="pl-s1">user_part</span>, <span class="pl-s1">query_part</span> <span class="pl-c1">=</span> <span class="pl-s1">user_text</span>.<span class="pl-c1">split</span>(<span class="pl-s">' says: '</span>, <span class="pl-c1">1</span>)</td>
        </tr>
        <tr>
          <td id="file-bedrock_log_enhancement-py-L36" class="blob-num js-line-number js-blob-rnum" data-line-number="36"></td>
          <td id="file-bedrock_log_enhancement-py-LC36" class="blob-code blob-code-inner js-file-line">                        <span class="pl-s1">enhanced_event</span>[<span class="pl-s">'user'</span>] <span class="pl-c1">=</span> <span class="pl-s1">user_part</span>.<span class="pl-c1">split</span>(<span class="pl-s">' ('</span>)[<span class="pl-c1">0</span>]</td>
        </tr>
        <tr>
          <td id="file-bedrock_log_enhancement-py-L37" class="blob-num js-line-number js-blob-rnum" data-line-number="37"></td>
          <td id="file-bedrock_log_enhancement-py-LC37" class="blob-code blob-code-inner js-file-line">                        <span class="pl-s1">enhanced_event</span>[<span class="pl-s">'query'</span>] <span class="pl-c1">=</span> <span class="pl-s1">query_part</span></td>
        </tr>
        <tr>
          <td id="file-bedrock_log_enhancement-py-L38" class="blob-num js-line-number js-blob-rnum" data-line-number="38"></td>
          <td id="file-bedrock_log_enhancement-py-LC38" class="blob-code blob-code-inner js-file-line">                    <span class="pl-k">break</span></td>
        </tr>
        <tr>
          <td id="file-bedrock_log_enhancement-py-L39" class="blob-num js-line-number js-blob-rnum" data-line-number="39"></td>
          <td id="file-bedrock_log_enhancement-py-LC39" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">except</span> (<span class="pl-v">KeyError</span>, <span class="pl-v">TypeError</span>, <span class="pl-v">IndexError</span>, <span class="pl-v">ValueError</span>):</td>
        </tr>
        <tr>
          <td id="file-bedrock_log_enhancement-py-L40" class="blob-num js-line-number js-blob-rnum" data-line-number="40"></td>
          <td id="file-bedrock_log_enhancement-py-LC40" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">pass</span></td>
        </tr>
        <tr>
          <td id="file-bedrock_log_enhancement-py-L41" class="blob-num js-line-number js-blob-rnum" data-line-number="41"></td>
          <td id="file-bedrock_log_enhancement-py-LC41" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">return</span> <span class="pl-s1">json</span>.<span class="pl-c1">dumps</span>(<span class="pl-s1">enhanced_event</span>)</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/e53599aed495806b61d03faa272f339a/raw/009b2586c7879bf003e27b33235bc9b2bef2954d/bedrock_log_enhancement.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/e53599aed495806b61d03faa272f339a#file-bedrock_log_enhancement-py" class="Link--inTextBlock">
          bedrock_log_enhancement.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>The code handles all the edge cases gracefully. If any field can&#8217;t be extracted, we just skip it rather than failing the entire log processing. This keeps the pipeline resilient when AWS changes log formats or when we encounter unexpected message structures.</p><p>What emerges from this parsing is a clean, searchable log entry that tells the complete story: which bot responded to which user&#8217;s query with what output. Instead of archaeological JSON excavation, you get logs that humans can actually read and monitoring systems can actually use.</p><h1><strong>Terraform Infrastructure: Making It Work</strong></h1><p>Setting up this pipeline requires several Terraform resources working together. The core piece is the Kinesis Firehose delivery stream that connects everything.</p><p><strong>CloudWatch Subscription Filter</strong></p><p>First, you need a subscription filter on the Bedrock log group that forwards all log events to your Firehose stream. This is the entry point that captures every Bedrock model invocation as it gets logged.</p><p><strong>IAM Roles and Permissions</strong></p><p>The pipeline requires two main IAM roles. One for CloudWatch to write to Firehose, and another for Firehose to invoke your Lambda processor and write to S3 for failed events. Standard AWS service-to-service permissions, but they need to be configured correctly or nothing works.</p><p><strong>Lambda Function</strong></p><p>Your log processing Lambda function needs the right runtime (Python 3.13), appropriate timeout settings, and environment variables for any custom configuration like Splunk source types.</p><p><strong>Kinesis Firehose Configuration</strong></p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist141198473\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-bedrock_logging_firehose-tf\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-hcl  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;bedrock_logging_firehose.tf content, created by KyMidd on 08:46PM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;4\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;bedrock_logging_firehose.tf\&quot;>\n        <tr>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=\&quot;pl-en\&quot;>resource</span> <span class=\&quot;pl-smi\&quot;>&amp;quot;aws_kinesis_firehose_delivery_stream&amp;quot;</span> <span class=\&quot;pl-smi\&quot;>&amp;quot;bedrock_logs&amp;quot;</span> {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  <span class=\&quot;pl-v\&quot;><span class=\&quot;pl-smi\&quot;>name</span>        <span class=\&quot;pl-k\&quot;>=</span> </span><span class=\&quot;pl-s\&quot;><span class=\&quot;pl-pds\&quot;>&amp;quot;</span>bedrock-logs-to-splunk<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  <span class=\&quot;pl-v\&quot;><span class=\&quot;pl-smi\&quot;>destination</span> <span class=\&quot;pl-k\&quot;>=</span> </span><span class=\&quot;pl-s\&quot;><span class=\&quot;pl-pds\&quot;>&amp;quot;</span>splunk<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  <span class=\&quot;pl-en\&quot;>splunk_configuration</span> {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=\&quot;pl-v\&quot;><span class=\&quot;pl-smi\&quot;>hec_endpoint</span>    <span class=\&quot;pl-k\&quot;>=</span> </span><span class=\&quot;pl-s\&quot;><span class=\&quot;pl-pds\&quot;>&amp;quot;</span>https://http-inputs-example.splunkcloud.com/services/collector/event:443<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=\&quot;pl-v\&quot;><span class=\&quot;pl-smi\&quot;>hec_token</span>       <span class=\&quot;pl-k\&quot;>=</span> </span>var<span class=\&quot;pl-k\&quot;>.</span><span class=\&quot;pl-smi\&quot;>splunk_hec_token</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=\&quot;pl-v\&quot;><span class=\&quot;pl-smi\&quot;>hec_endpoint_type</span> <span class=\&quot;pl-k\&quot;>=</span> </span><span class=\&quot;pl-s\&quot;><span class=\&quot;pl-pds\&quot;>&amp;quot;</span>Event<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=\&quot;pl-v\&quot;><span class=\&quot;pl-smi\&quot;>s3_backup_mode</span>    <span class=\&quot;pl-k\&quot;>=</span> </span><span class=\&quot;pl-s\&quot;><span class=\&quot;pl-pds\&quot;>&amp;quot;</span>FailedEventsOnly<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=\&quot;pl-en\&quot;>processing_configuration</span> {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>      <span class=\&quot;pl-v\&quot;><span class=\&quot;pl-smi\&quot;>enabled</span> <span class=\&quot;pl-k\&quot;>=</span> </span><span class=\&quot;pl-c1\&quot;>true</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>      <span class=\&quot;pl-en\&quot;>processors</span> {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=\&quot;pl-v\&quot;><span class=\&quot;pl-smi\&quot;>type</span> <span class=\&quot;pl-k\&quot;>=</span> </span><span class=\&quot;pl-s\&quot;><span class=\&quot;pl-pds\&quot;>&amp;quot;</span>Lambda<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-L16\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;16\&quot;></td>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-L17\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;17\&quot;></td>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=\&quot;pl-en\&quot;>parameters</span> {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-L18\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;18\&quot;></td>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-LC18\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>          <span class=\&quot;pl-v\&quot;><span class=\&quot;pl-smi\&quot;>parameter_name</span>  <span class=\&quot;pl-k\&quot;>=</span> </span><span class=\&quot;pl-s\&quot;><span class=\&quot;pl-pds\&quot;>&amp;quot;</span>LambdaArn<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-L19\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;19\&quot;></td>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-LC19\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>          <span class=\&quot;pl-v\&quot;><span class=\&quot;pl-smi\&quot;>parameter_value</span> <span class=\&quot;pl-k\&quot;>=</span> </span>aws_lambda_function<span class=\&quot;pl-k\&quot;>.</span><span class=\&quot;pl-smi\&quot;>cw_log_processor</span><span class=\&quot;pl-k\&quot;>.</span><span class=\&quot;pl-smi\&quot;>arn</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-L20\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;20\&quot;></td>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-LC20\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        }</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-L21\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;21\&quot;></td>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-LC21\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>      }</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-L22\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;22\&quot;></td>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-LC22\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    }</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-L23\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;23\&quot;></td>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-LC23\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-L24\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;24\&quot;></td>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-LC24\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=\&quot;pl-en\&quot;>s3_configuration</span> {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-L25\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;25\&quot;></td>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-LC25\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>      <span class=\&quot;pl-v\&quot;><span class=\&quot;pl-smi\&quot;>role_arn</span>           <span class=\&quot;pl-k\&quot;>=</span> </span>aws_iam_role<span class=\&quot;pl-k\&quot;>.</span><span class=\&quot;pl-smi\&quot;>firehose_delivery_role</span><span class=\&quot;pl-k\&quot;>.</span><span class=\&quot;pl-smi\&quot;>arn</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-L26\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;26\&quot;></td>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-LC26\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>      <span class=\&quot;pl-v\&quot;><span class=\&quot;pl-smi\&quot;>bucket_arn</span>         <span class=\&quot;pl-k\&quot;>=</span> </span>aws_s3_bucket<span class=\&quot;pl-k\&quot;>.</span><span class=\&quot;pl-smi\&quot;>failed_logs</span><span class=\&quot;pl-k\&quot;>.</span><span class=\&quot;pl-smi\&quot;>arn</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-L27\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;27\&quot;></td>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-LC27\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>      <span class=\&quot;pl-v\&quot;><span class=\&quot;pl-smi\&quot;>prefix</span>             <span class=\&quot;pl-k\&quot;>=</span> </span><span class=\&quot;pl-s\&quot;><span class=\&quot;pl-pds\&quot;>&amp;quot;</span>failed-bedrock-logs/<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-L28\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;28\&quot;></td>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-LC28\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>      <span class=\&quot;pl-v\&quot;><span class=\&quot;pl-smi\&quot;>error_output_prefix</span> <span class=\&quot;pl-k\&quot;>=</span> </span><span class=\&quot;pl-s\&quot;><span class=\&quot;pl-pds\&quot;>&amp;quot;</span>errors/<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-L29\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;29\&quot;></td>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-LC29\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>      <span class=\&quot;pl-v\&quot;><span class=\&quot;pl-smi\&quot;>buffer_size</span>        <span class=\&quot;pl-k\&quot;>=</span> </span><span class=\&quot;pl-c1\&quot;>5</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-L30\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;30\&quot;></td>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-LC30\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>      <span class=\&quot;pl-v\&quot;><span class=\&quot;pl-smi\&quot;>buffer_interval</span>    <span class=\&quot;pl-k\&quot;>=</span> </span><span class=\&quot;pl-c1\&quot;>300</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-L31\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;31\&quot;></td>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-LC31\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    }</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-L32\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;32\&quot;></td>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-LC32\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  }</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-L33\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;33\&quot;></td>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-LC33\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-L34\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;34\&quot;></td>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-LC34\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  <span class=\&quot;pl-en\&quot;>server_side_encryption</span> {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-L35\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;35\&quot;></td>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-LC35\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=\&quot;pl-v\&quot;><span class=\&quot;pl-smi\&quot;>enabled</span> <span class=\&quot;pl-k\&quot;>=</span> </span><span class=\&quot;pl-c1\&quot;>true</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-L36\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;36\&quot;></td>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-LC36\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  }</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-L37\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;37\&quot;></td>\n          <td id=\&quot;file-bedrock_logging_firehose-tf-LC37\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>}</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/7c647a891e34b056263ec18348e5cda0/raw/54c0906882c78d0e701b023b8e4e496c798ec81d/bedrock_logging_firehose.tf\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/7c647a891e34b056263ec18348e5cda0#file-bedrock_logging_firehose-tf\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          bedrock_logging_firehose.tf\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-59543e005c9c.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-59543e005c9c.css"><div id="gist141198473" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-bedrock_logging_firehose-tf" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-hcl  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="4" data-paste-markdown-skip="" data-tagsearch-path="bedrock_logging_firehose.tf">
        <tbody><tr>
          <td id="file-bedrock_logging_firehose-tf-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-bedrock_logging_firehose-tf-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-en">resource</span> <span class="pl-smi">"aws_kinesis_firehose_delivery_stream"</span> <span class="pl-smi">"bedrock_logs"</span> {</td>
        </tr>
        <tr>
          <td id="file-bedrock_logging_firehose-tf-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-bedrock_logging_firehose-tf-LC2" class="blob-code blob-code-inner js-file-line">  <span class="pl-v"><span class="pl-smi">name</span>        <span class="pl-k">=</span> </span><span class="pl-s"><span class="pl-pds">"</span>bedrock-logs-to-splunk<span class="pl-pds">"</span></span></td>
        </tr>
        <tr>
          <td id="file-bedrock_logging_firehose-tf-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-bedrock_logging_firehose-tf-LC3" class="blob-code blob-code-inner js-file-line">  <span class="pl-v"><span class="pl-smi">destination</span> <span class="pl-k">=</span> </span><span class="pl-s"><span class="pl-pds">"</span>splunk<span class="pl-pds">"</span></span></td>
        </tr>
        <tr>
          <td id="file-bedrock_logging_firehose-tf-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-bedrock_logging_firehose-tf-LC4" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-bedrock_logging_firehose-tf-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-bedrock_logging_firehose-tf-LC5" class="blob-code blob-code-inner js-file-line">  <span class="pl-en">splunk_configuration</span> {</td>
        </tr>
        <tr>
          <td id="file-bedrock_logging_firehose-tf-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-bedrock_logging_firehose-tf-LC6" class="blob-code blob-code-inner js-file-line">    <span class="pl-v"><span class="pl-smi">hec_endpoint</span>    <span class="pl-k">=</span> </span><span class="pl-s"><span class="pl-pds">"</span>https://http-inputs-example.splunkcloud.com/services/collector/event:443<span class="pl-pds">"</span></span></td>
        </tr>
        <tr>
          <td id="file-bedrock_logging_firehose-tf-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-bedrock_logging_firehose-tf-LC7" class="blob-code blob-code-inner js-file-line">    <span class="pl-v"><span class="pl-smi">hec_token</span>       <span class="pl-k">=</span> </span>var<span class="pl-k">.</span><span class="pl-smi">splunk_hec_token</span></td>
        </tr>
        <tr>
          <td id="file-bedrock_logging_firehose-tf-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-bedrock_logging_firehose-tf-LC8" class="blob-code blob-code-inner js-file-line">    <span class="pl-v"><span class="pl-smi">hec_endpoint_type</span> <span class="pl-k">=</span> </span><span class="pl-s"><span class="pl-pds">"</span>Event<span class="pl-pds">"</span></span></td>
        </tr>
        <tr>
          <td id="file-bedrock_logging_firehose-tf-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-bedrock_logging_firehose-tf-LC9" class="blob-code blob-code-inner js-file-line">    <span class="pl-v"><span class="pl-smi">s3_backup_mode</span>    <span class="pl-k">=</span> </span><span class="pl-s"><span class="pl-pds">"</span>FailedEventsOnly<span class="pl-pds">"</span></span></td>
        </tr>
        <tr>
          <td id="file-bedrock_logging_firehose-tf-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-bedrock_logging_firehose-tf-LC10" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-bedrock_logging_firehose-tf-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-bedrock_logging_firehose-tf-LC11" class="blob-code blob-code-inner js-file-line">    <span class="pl-en">processing_configuration</span> {</td>
        </tr>
        <tr>
          <td id="file-bedrock_logging_firehose-tf-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-bedrock_logging_firehose-tf-LC12" class="blob-code blob-code-inner js-file-line">      <span class="pl-v"><span class="pl-smi">enabled</span> <span class="pl-k">=</span> </span><span class="pl-c1">true</span></td>
        </tr>
        <tr>
          <td id="file-bedrock_logging_firehose-tf-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-bedrock_logging_firehose-tf-LC13" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-bedrock_logging_firehose-tf-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-bedrock_logging_firehose-tf-LC14" class="blob-code blob-code-inner js-file-line">      <span class="pl-en">processors</span> {</td>
        </tr>
        <tr>
          <td id="file-bedrock_logging_firehose-tf-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-bedrock_logging_firehose-tf-LC15" class="blob-code blob-code-inner js-file-line">        <span class="pl-v"><span class="pl-smi">type</span> <span class="pl-k">=</span> </span><span class="pl-s"><span class="pl-pds">"</span>Lambda<span class="pl-pds">"</span></span></td>
        </tr>
        <tr>
          <td id="file-bedrock_logging_firehose-tf-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-bedrock_logging_firehose-tf-LC16" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-bedrock_logging_firehose-tf-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-bedrock_logging_firehose-tf-LC17" class="blob-code blob-code-inner js-file-line">        <span class="pl-en">parameters</span> {</td>
        </tr>
        <tr>
          <td id="file-bedrock_logging_firehose-tf-L18" class="blob-num js-line-number js-blob-rnum" data-line-number="18"></td>
          <td id="file-bedrock_logging_firehose-tf-LC18" class="blob-code blob-code-inner js-file-line">          <span class="pl-v"><span class="pl-smi">parameter_name</span>  <span class="pl-k">=</span> </span><span class="pl-s"><span class="pl-pds">"</span>LambdaArn<span class="pl-pds">"</span></span></td>
        </tr>
        <tr>
          <td id="file-bedrock_logging_firehose-tf-L19" class="blob-num js-line-number js-blob-rnum" data-line-number="19"></td>
          <td id="file-bedrock_logging_firehose-tf-LC19" class="blob-code blob-code-inner js-file-line">          <span class="pl-v"><span class="pl-smi">parameter_value</span> <span class="pl-k">=</span> </span>aws_lambda_function<span class="pl-k">.</span><span class="pl-smi">cw_log_processor</span><span class="pl-k">.</span><span class="pl-smi">arn</span></td>
        </tr>
        <tr>
          <td id="file-bedrock_logging_firehose-tf-L20" class="blob-num js-line-number js-blob-rnum" data-line-number="20"></td>
          <td id="file-bedrock_logging_firehose-tf-LC20" class="blob-code blob-code-inner js-file-line">        }</td>
        </tr>
        <tr>
          <td id="file-bedrock_logging_firehose-tf-L21" class="blob-num js-line-number js-blob-rnum" data-line-number="21"></td>
          <td id="file-bedrock_logging_firehose-tf-LC21" class="blob-code blob-code-inner js-file-line">      }</td>
        </tr>
        <tr>
          <td id="file-bedrock_logging_firehose-tf-L22" class="blob-num js-line-number js-blob-rnum" data-line-number="22"></td>
          <td id="file-bedrock_logging_firehose-tf-LC22" class="blob-code blob-code-inner js-file-line">    }</td>
        </tr>
        <tr>
          <td id="file-bedrock_logging_firehose-tf-L23" class="blob-num js-line-number js-blob-rnum" data-line-number="23"></td>
          <td id="file-bedrock_logging_firehose-tf-LC23" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-bedrock_logging_firehose-tf-L24" class="blob-num js-line-number js-blob-rnum" data-line-number="24"></td>
          <td id="file-bedrock_logging_firehose-tf-LC24" class="blob-code blob-code-inner js-file-line">    <span class="pl-en">s3_configuration</span> {</td>
        </tr>
        <tr>
          <td id="file-bedrock_logging_firehose-tf-L25" class="blob-num js-line-number js-blob-rnum" data-line-number="25"></td>
          <td id="file-bedrock_logging_firehose-tf-LC25" class="blob-code blob-code-inner js-file-line">      <span class="pl-v"><span class="pl-smi">role_arn</span>           <span class="pl-k">=</span> </span>aws_iam_role<span class="pl-k">.</span><span class="pl-smi">firehose_delivery_role</span><span class="pl-k">.</span><span class="pl-smi">arn</span></td>
        </tr>
        <tr>
          <td id="file-bedrock_logging_firehose-tf-L26" class="blob-num js-line-number js-blob-rnum" data-line-number="26"></td>
          <td id="file-bedrock_logging_firehose-tf-LC26" class="blob-code blob-code-inner js-file-line">      <span class="pl-v"><span class="pl-smi">bucket_arn</span>         <span class="pl-k">=</span> </span>aws_s3_bucket<span class="pl-k">.</span><span class="pl-smi">failed_logs</span><span class="pl-k">.</span><span class="pl-smi">arn</span></td>
        </tr>
        <tr>
          <td id="file-bedrock_logging_firehose-tf-L27" class="blob-num js-line-number js-blob-rnum" data-line-number="27"></td>
          <td id="file-bedrock_logging_firehose-tf-LC27" class="blob-code blob-code-inner js-file-line">      <span class="pl-v"><span class="pl-smi">prefix</span>             <span class="pl-k">=</span> </span><span class="pl-s"><span class="pl-pds">"</span>failed-bedrock-logs/<span class="pl-pds">"</span></span></td>
        </tr>
        <tr>
          <td id="file-bedrock_logging_firehose-tf-L28" class="blob-num js-line-number js-blob-rnum" data-line-number="28"></td>
          <td id="file-bedrock_logging_firehose-tf-LC28" class="blob-code blob-code-inner js-file-line">      <span class="pl-v"><span class="pl-smi">error_output_prefix</span> <span class="pl-k">=</span> </span><span class="pl-s"><span class="pl-pds">"</span>errors/<span class="pl-pds">"</span></span></td>
        </tr>
        <tr>
          <td id="file-bedrock_logging_firehose-tf-L29" class="blob-num js-line-number js-blob-rnum" data-line-number="29"></td>
          <td id="file-bedrock_logging_firehose-tf-LC29" class="blob-code blob-code-inner js-file-line">      <span class="pl-v"><span class="pl-smi">buffer_size</span>        <span class="pl-k">=</span> </span><span class="pl-c1">5</span></td>
        </tr>
        <tr>
          <td id="file-bedrock_logging_firehose-tf-L30" class="blob-num js-line-number js-blob-rnum" data-line-number="30"></td>
          <td id="file-bedrock_logging_firehose-tf-LC30" class="blob-code blob-code-inner js-file-line">      <span class="pl-v"><span class="pl-smi">buffer_interval</span>    <span class="pl-k">=</span> </span><span class="pl-c1">300</span></td>
        </tr>
        <tr>
          <td id="file-bedrock_logging_firehose-tf-L31" class="blob-num js-line-number js-blob-rnum" data-line-number="31"></td>
          <td id="file-bedrock_logging_firehose-tf-LC31" class="blob-code blob-code-inner js-file-line">    }</td>
        </tr>
        <tr>
          <td id="file-bedrock_logging_firehose-tf-L32" class="blob-num js-line-number js-blob-rnum" data-line-number="32"></td>
          <td id="file-bedrock_logging_firehose-tf-LC32" class="blob-code blob-code-inner js-file-line">  }</td>
        </tr>
        <tr>
          <td id="file-bedrock_logging_firehose-tf-L33" class="blob-num js-line-number js-blob-rnum" data-line-number="33"></td>
          <td id="file-bedrock_logging_firehose-tf-LC33" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-bedrock_logging_firehose-tf-L34" class="blob-num js-line-number js-blob-rnum" data-line-number="34"></td>
          <td id="file-bedrock_logging_firehose-tf-LC34" class="blob-code blob-code-inner js-file-line">  <span class="pl-en">server_side_encryption</span> {</td>
        </tr>
        <tr>
          <td id="file-bedrock_logging_firehose-tf-L35" class="blob-num js-line-number js-blob-rnum" data-line-number="35"></td>
          <td id="file-bedrock_logging_firehose-tf-LC35" class="blob-code blob-code-inner js-file-line">    <span class="pl-v"><span class="pl-smi">enabled</span> <span class="pl-k">=</span> </span><span class="pl-c1">true</span></td>
        </tr>
        <tr>
          <td id="file-bedrock_logging_firehose-tf-L36" class="blob-num js-line-number js-blob-rnum" data-line-number="36"></td>
          <td id="file-bedrock_logging_firehose-tf-LC36" class="blob-code blob-code-inner js-file-line">  }</td>
        </tr>
        <tr>
          <td id="file-bedrock_logging_firehose-tf-L37" class="blob-num js-line-number js-blob-rnum" data-line-number="37"></td>
          <td id="file-bedrock_logging_firehose-tf-LC37" class="blob-code blob-code-inner js-file-line">}</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/7c647a891e34b056263ec18348e5cda0/raw/54c0906882c78d0e701b023b8e4e496c798ec81d/bedrock_logging_firehose.tf" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/7c647a891e34b056263ec18348e5cda0#file-bedrock_logging_firehose-tf" class="Link--inTextBlock">
          bedrock_logging_firehose.tf
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>The Firehose configuration is where everything comes together. You specify your Splunk endpoint, configure the Lambda processor, and set up S3 backup for failed events. The processing configuration is key - that&#8217;s what tells Firehose to run your custom Lambda function on every log batch before sending to Splunk.</p><p><strong>Error Handling</strong></p><p>Don&#8217;t forget S3 backup for failed events. When something goes wrong with log processing or Splunk delivery, you want those logs going somewhere you can investigate later rather than disappearing into the void.</p><h1><strong>Real-World Impact and Benefits</strong></h1><p>The difference this makes in day-to-day operations is dramatic.</p><p><strong>Individual Bot Monitoring</strong></p><p>Suddenly, you can answer basic questions about your AI deployments. &#8220;How much is the customer support bot costing us?&#8221; becomes a simple Splunk query filtering by `botName`. &#8220;Which bot is getting the most usage?&#8221; is just a matter of counting log entries by bot. No more spreadsheet gymnastics or manual log parsing.</p><p><strong>Troubleshooting User Interactions</strong></p><p>When someone reports &#8220;the bot gave me a weird answer,&#8221; you can actually find their interaction. Search for their username and the approximate time, and there&#8217;s the complete conversation - what they asked, what the bot responded with, and all the underlying model invocation details. This turns impossible debugging sessions into five-minute investigations.</p><p><strong>Cost Attribution and Chargeback</strong></p><p>Enterprise teams love being able to track costs by department or project. With botName extraction, you can build dashboards that show exactly how much each team is spending on AI. Finance gets happy, teams get visibility into their usage, and everyone makes better decisions about resource allocation.</p><p><strong>Performance Insights</strong></p><p>The enriched logs reveal usage patterns you never knew existed. Which types of queries take the longest to process? What time of day sees the most bot activity? Are certain users asking the same questions repeatedly (hint: maybe your documentation needs work)?</p><p><strong>Integration with Existing Monitoring</strong></p><p>Because the enhanced logs flow into whatever monitoring system you&#8217;re already using, all your existing dashboards, alerts, and processes just work. Set up alerts when a specific bot starts throwing errors. Create dashboards showing conversation trends across different teams. Build reports on AI adoption across the organization.</p><p><strong>Conversation Analytics</strong></p><p>Having the actual user queries and bot responses in searchable logs opens up possibilities for conversation analytics. You can identify common question patterns, track how bot responses change over time, and even spot opportunities for better training data or system improvements.</p><p>The end result is that AWS Bedrock goes from being a black box that costs money to being a transparent, manageable enterprise service that you can actually operate with confidence.</p><h1><strong>Summary</strong></h1><p>AWS Bedrock dumps all model invocation logs into a single regional CloudWatch log group, making enterprise monitoring nearly impossible. This creates chaos when trying to track costs, troubleshoot issues, or monitor multiple bots.</p><p>The solution: a Kinesis Firehose pipeline with a custom Lambda processor that intercepts logs flowing to Splunk and adds essential context. The processor extracts botName (from IAM role), botOutput (actual response), user (who asked), and query (what they asked) from the raw JSON.</p><p>This transforms cryptic Bedrock logs into actionable monitoring data, enabling cost attribution, user interaction troubleshooting, and conversation analytics for enterprise AI deployments.</p><p>Thanks all!<br>kyler</p><p></p><p></p><p></p><p></p>]]></content:encoded></item><item><title><![CDATA[🔥Building an Enterprise Slack Bot with Agentic AI: A Complete AWS Architecture Guide🔥]]></title><description><![CDATA[aka, find outages in PagerDuty, identify changes in GitHub, find tickets in Jira, tie em all together]]></description><link>https://www.letsdodevops.com/p/building-agentic-slack-bot</link><guid isPermaLink="false">https://www.letsdodevops.com/p/building-agentic-slack-bot</guid><dc:creator><![CDATA[Kyler Middleton]]></dc:creator><pubDate>Tue, 30 Sep 2025 14:31:26 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!hfuR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9399b65-672f-4fee-979a-02c405e7398e_1472x1172.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote><p><em>This blog series focuses on presenting complex DevOps projects as simple and approachable via plain language and lots of pictures. You can do it!</em></p><p>These articles are supported by readers, <a href="https://www.letsdodevops.com/subscribe">please consider subscribing</a> to support me writing more of these articles &lt;3 :)</p></blockquote><p>Hey all! </p><p>Today we&#8217;re going to explore something pretty exciting in the world of enterprise AI applications. I recently got my hands on an evolution of the Vera genAI solution that I&#8217;ve covered extensively before - and this new version represents a significant architectural leap forward.</p><p>If you&#8217;ve been following my previous posts about Vera, you&#8217;ll remember it was built with Python directly without many external libraries, focusing on raw AWS Bedrock integration. This new implementation takes that foundation and transforms it into a proper agentic AI system using the Strands framework. Instead of just being a conversational AI, it can now take actions and use tools across your entire enterprise toolchain.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!hfuR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9399b65-672f-4fee-979a-02c405e7398e_1472x1172.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!hfuR!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9399b65-672f-4fee-979a-02c405e7398e_1472x1172.png 424w, https://substackcdn.com/image/fetch/$s_!hfuR!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9399b65-672f-4fee-979a-02c405e7398e_1472x1172.png 848w, https://substackcdn.com/image/fetch/$s_!hfuR!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9399b65-672f-4fee-979a-02c405e7398e_1472x1172.png 1272w, https://substackcdn.com/image/fetch/$s_!hfuR!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9399b65-672f-4fee-979a-02c405e7398e_1472x1172.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!hfuR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9399b65-672f-4fee-979a-02c405e7398e_1472x1172.png" width="1456" height="1159" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f9399b65-672f-4fee-979a-02c405e7398e_1472x1172.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1159,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:373096,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.letsdodevops.com/i/174558992?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9399b65-672f-4fee-979a-02c405e7398e_1472x1172.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!hfuR!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9399b65-672f-4fee-979a-02c405e7398e_1472x1172.png 424w, https://substackcdn.com/image/fetch/$s_!hfuR!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9399b65-672f-4fee-979a-02c405e7398e_1472x1172.png 848w, https://substackcdn.com/image/fetch/$s_!hfuR!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9399b65-672f-4fee-979a-02c405e7398e_1472x1172.png 1272w, https://substackcdn.com/image/fetch/$s_!hfuR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff9399b65-672f-4fee-979a-02c405e7398e_1472x1172.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>What makes this implementation particularly interesting is how it solves those everyday enterprise pain points we all know too well. You know that juggling act between Slack, PagerDuty, GitHub, and Jira just to get context on a single issue? This bot brings all those systems together in one conversational interface, powered by AWS Bedrock with Claude Sonnet 4 as the backend.</p><p>Here&#8217;s where the agentic capabilities really shine. The bot uses Model Context Protocol (MCP) to actually connect to your existing platforms. When someone asks &#8220;what incidents are assigned to me?&#8221; it goes and queries PagerDuty directly through AWS Bedrock&#8217;s processing. When they want to know about pull request status, it hits the GitHub API. The AI becomes a conversational interface to your entire toolchain, all orchestrated through AWS Bedrock&#8217;s model capabilities.</p><p>The system includes AWS Bedrock Guardrails for content filtering and even supports retrieval-augmented generation through Bedrock Knowledge Bases. It&#8217;s engineered for enterprise use with proper secret management, brand voice guidelines, and all the infrastructure automation you&#8217;d expect in a production environment.</p><p>In this article, we&#8217;ll walk through the complete architecture, examine how the Strands agentic framework transforms the original Vera approach, and look at how AWS Bedrock powers the multi-service integrations. Whether you&#8217;re thinking about building your own AI assistant or just curious about modern agentic architectures, this implementation has some great lessons to offer.</p><p>Let&#8217;s jump in and see how this all fits together!</p><p>If you want to skip the write-up and just read the code, you can find that here: </p><blockquote><p><a href="https://github.com/KyMidd/SlackStrandsAgenticBot">github.com/KyMidd/SlackStrandsAgenticBot</a></p></blockquote><h1><strong>Containerizing the Worker Lambda: Why Docker Over Layers</strong></h1><p>When I started building this enhanced version of Vera, I quickly ran into a problem that anyone working with modern Python AI frameworks knows all too well: dependency hell.</p><p>The original Vera was relatively lightweight - just AWS SDK calls and some basic Slack integration. But this agentic version? We&#8217;re talking about the Strands framework, multiple MCP clients for different services, and a whole ecosystem of dependencies that each service brings along. The requirements.txt file exploded from a handful of packages to something that looked more like a small novel.</p><p>Lambda layers seemed like the obvious choice at first. After all, that&#8217;s what they&#8217;re designed for - sharing common dependencies across functions. But here&#8217;s where things got messy: we&#8217;re running on ARM64 for cost savings, and compiling some of these Python packages for ARM64 is a nightmare. Half the dependencies don&#8217;t have ARM64 wheels available, which means building from source. And building from source on ARM64? Good luck with that.</p><p>I spent way too much time trying to get a clean layer build working before I admitted defeat and switched to containers. Suddenly, all those dependency headaches disappeared. The AWS Lambda Python runtime base image handles the ARM64 compilation complexity, and I can use modern Python package managers like `uv` to handle the dependency resolution efficiently.</p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist141196942\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-dockerfile\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-dockerfile  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;Dockerfile content, created by KyMidd on 07:30PM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;4\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;Dockerfile\&quot;>\n        <tr>\n          <td id=\&quot;file-dockerfile-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-dockerfile-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=\&quot;pl-c\&quot;><span class=\&quot;pl-c\&quot;>#</span> Use AWS Lambda Python runtime as base image for ARM64</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-dockerfile-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-dockerfile-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=\&quot;pl-k\&quot;>FROM</span> public.ecr.aws/lambda/python:3.12-arm64</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-dockerfile-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-dockerfile-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-dockerfile-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-dockerfile-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=\&quot;pl-c\&quot;><span class=\&quot;pl-c\&quot;>#</span> Ensure system packages needed to fetch/install uv are present (AL2023 uses dnf)</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-dockerfile-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-dockerfile-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=\&quot;pl-k\&quot;>RUN</span> dnf -y install ca-certificates tar gzip &amp;amp;&amp;amp; dnf clean all</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-dockerfile-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-dockerfile-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-dockerfile-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-dockerfile-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=\&quot;pl-c\&quot;><span class=\&quot;pl-c\&quot;>#</span> Install uv (installs to /root/.local/bin by default)</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-dockerfile-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-dockerfile-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=\&quot;pl-k\&quot;>RUN</span> curl -LsSf https://astral.sh/uv/install.sh | sh -s --</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-dockerfile-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-dockerfile-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-dockerfile-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-dockerfile-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=\&quot;pl-c\&quot;><span class=\&quot;pl-c\&quot;>#</span> Make uv readable and executable by all users</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-dockerfile-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-dockerfile-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=\&quot;pl-k\&quot;>RUN</span> install -m 0755 /root/.local/bin/uv  /usr/local/bin/uv \\</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-dockerfile-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-dockerfile-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    &amp;amp;&amp;amp; install -m 0755 /root/.local/bin/uvx /usr/local/bin/uvx</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-dockerfile-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-dockerfile-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-dockerfile-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-dockerfile-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=\&quot;pl-c\&quot;><span class=\&quot;pl-c\&quot;>#</span> Put uv/uvx in PATH</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-dockerfile-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-dockerfile-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=\&quot;pl-k\&quot;>ENV</span> PATH=<span class=\&quot;pl-s\&quot;>&amp;quot;/root/.local/bin:${PATH}&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-dockerfile-L16\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;16\&quot;></td>\n          <td id=\&quot;file-dockerfile-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-dockerfile-L17\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;17\&quot;></td>\n          <td id=\&quot;file-dockerfile-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=\&quot;pl-c\&quot;><span class=\&quot;pl-c\&quot;>#</span> Verify uv installation</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-dockerfile-L18\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;18\&quot;></td>\n          <td id=\&quot;file-dockerfile-LC18\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=\&quot;pl-k\&quot;>RUN</span> uvx --version</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-dockerfile-L19\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;19\&quot;></td>\n          <td id=\&quot;file-dockerfile-LC19\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-dockerfile-L20\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;20\&quot;></td>\n          <td id=\&quot;file-dockerfile-LC20\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=\&quot;pl-c\&quot;><span class=\&quot;pl-c\&quot;>#</span> Set environment variables</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-dockerfile-L21\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;21\&quot;></td>\n          <td id=\&quot;file-dockerfile-LC21\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=\&quot;pl-k\&quot;>ENV</span> PYTHONPATH=<span class=\&quot;pl-s\&quot;>&amp;quot;${LAMBDA_TASK_ROOT}&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-dockerfile-L22\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;22\&quot;></td>\n          <td id=\&quot;file-dockerfile-LC22\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-dockerfile-L23\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;23\&quot;></td>\n          <td id=\&quot;file-dockerfile-LC23\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=\&quot;pl-c\&quot;><span class=\&quot;pl-c\&quot;>#</span> Copy requirements file</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-dockerfile-L24\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;24\&quot;></td>\n          <td id=\&quot;file-dockerfile-LC24\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=\&quot;pl-k\&quot;>COPY</span> requirements.txt ${LAMBDA_TASK_ROOT}/</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-dockerfile-L25\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;25\&quot;></td>\n          <td id=\&quot;file-dockerfile-LC25\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-dockerfile-L26\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;26\&quot;></td>\n          <td id=\&quot;file-dockerfile-LC26\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=\&quot;pl-c\&quot;><span class=\&quot;pl-c\&quot;>#</span> Install Python dependencies</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-dockerfile-L27\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;27\&quot;></td>\n          <td id=\&quot;file-dockerfile-LC27\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=\&quot;pl-k\&quot;>RUN</span> pip install --no-cache-dir -r requirements.txt \\</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-dockerfile-L28\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;28\&quot;></td>\n          <td id=\&quot;file-dockerfile-LC28\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=\&quot;pl-c\&quot;><span class=\&quot;pl-c\&quot;>#</span> Clean up to reduce image size</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-dockerfile-L29\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;29\&quot;></td>\n          <td id=\&quot;file-dockerfile-LC29\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    &amp;amp;&amp;amp; find /var/lang/lib/python3.12/site-packages -name <span class=\&quot;pl-s\&quot;>&amp;quot;*.pyc&amp;quot;</span> -delete \\</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-dockerfile-L30\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;30\&quot;></td>\n          <td id=\&quot;file-dockerfile-LC30\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    &amp;amp;&amp;amp; find /var/lang/lib/python3.12/site-packages -name <span class=\&quot;pl-s\&quot;>&amp;quot;__pycache__&amp;quot;</span> -exec rm -rf {} + 2&amp;gt;/dev/null || true \\</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-dockerfile-L31\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;31\&quot;></td>\n          <td id=\&quot;file-dockerfile-LC31\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    &amp;amp;&amp;amp; find /var/lang/lib/python3.12/site-packages -name <span class=\&quot;pl-s\&quot;>&amp;quot;test*&amp;quot;</span> -exec rm -rf {} + 2&amp;gt;/dev/null || true</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-dockerfile-L32\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;32\&quot;></td>\n          <td id=\&quot;file-dockerfile-LC32\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-dockerfile-L33\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;33\&quot;></td>\n          <td id=\&quot;file-dockerfile-LC33\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=\&quot;pl-c\&quot;><span class=\&quot;pl-c\&quot;>#</span> Copy function code</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-dockerfile-L34\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;34\&quot;></td>\n          <td id=\&quot;file-dockerfile-LC34\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=\&quot;pl-k\&quot;>COPY</span> src/*.py ${LAMBDA_TASK_ROOT}/</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-dockerfile-L35\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;35\&quot;></td>\n          <td id=\&quot;file-dockerfile-LC35\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-dockerfile-L36\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;36\&quot;></td>\n          <td id=\&quot;file-dockerfile-LC36\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=\&quot;pl-c\&quot;><span class=\&quot;pl-c\&quot;>#</span> Copy PagerDuty MCP server and pre-install dependencies</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-dockerfile-L37\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;37\&quot;></td>\n          <td id=\&quot;file-dockerfile-LC37\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=\&quot;pl-k\&quot;>COPY</span> pagerduty-mcp-server /opt/pagerduty-mcp-server</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-dockerfile-L38\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;38\&quot;></td>\n          <td id=\&quot;file-dockerfile-LC38\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=\&quot;pl-k\&quot;>RUN</span> cd /opt/pagerduty-mcp-server &amp;amp;&amp;amp; \\</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-dockerfile-L39\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;39\&quot;></td>\n          <td id=\&quot;file-dockerfile-LC39\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    uv sync --frozen &amp;amp;&amp;amp; \\</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-dockerfile-L40\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;40\&quot;></td>\n          <td id=\&quot;file-dockerfile-LC40\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    chmod -R a+rX /opt/pagerduty-mcp-server</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-dockerfile-L41\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;41\&quot;></td>\n          <td id=\&quot;file-dockerfile-LC41\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-dockerfile-L42\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;42\&quot;></td>\n          <td id=\&quot;file-dockerfile-LC42\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=\&quot;pl-c\&quot;><span class=\&quot;pl-c\&quot;>#</span> Set the CMD to your handler</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-dockerfile-L43\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;43\&quot;></td>\n          <td id=\&quot;file-dockerfile-LC43\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=\&quot;pl-k\&quot;>CMD</span> [<span class=\&quot;pl-s\&quot;>&amp;quot;worker.lambda_handler&amp;quot;</span>]</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/4385738ed45b998e71b3da16a73e4e55/raw/3a0399350d3c74b73ad871c0a93bd929cb53bddc/Dockerfile\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/4385738ed45b998e71b3da16a73e4e55#file-dockerfile\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          Dockerfile\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-59543e005c9c.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-59543e005c9c.css"><div id="gist141196942" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-dockerfile" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-dockerfile  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="4" data-paste-markdown-skip="" data-tagsearch-path="Dockerfile">
        <tbody><tr>
          <td id="file-dockerfile-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-dockerfile-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-c"><span class="pl-c">#</span> Use AWS Lambda Python runtime as base image for ARM64</span></td>
        </tr>
        <tr>
          <td id="file-dockerfile-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-dockerfile-LC2" class="blob-code blob-code-inner js-file-line"><span class="pl-k">FROM</span> public.ecr.aws/lambda/python:3.12-arm64</td>
        </tr>
        <tr>
          <td id="file-dockerfile-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-dockerfile-LC3" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-dockerfile-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-dockerfile-LC4" class="blob-code blob-code-inner js-file-line"><span class="pl-c"><span class="pl-c">#</span> Ensure system packages needed to fetch/install uv are present (AL2023 uses dnf)</span></td>
        </tr>
        <tr>
          <td id="file-dockerfile-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-dockerfile-LC5" class="blob-code blob-code-inner js-file-line"><span class="pl-k">RUN</span> dnf -y install ca-certificates tar gzip &amp;&amp; dnf clean all</td>
        </tr>
        <tr>
          <td id="file-dockerfile-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-dockerfile-LC6" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-dockerfile-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-dockerfile-LC7" class="blob-code blob-code-inner js-file-line"><span class="pl-c"><span class="pl-c">#</span> Install uv (installs to /root/.local/bin by default)</span></td>
        </tr>
        <tr>
          <td id="file-dockerfile-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-dockerfile-LC8" class="blob-code blob-code-inner js-file-line"><span class="pl-k">RUN</span> curl -LsSf https://astral.sh/uv/install.sh | sh -s --</td>
        </tr>
        <tr>
          <td id="file-dockerfile-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-dockerfile-LC9" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-dockerfile-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-dockerfile-LC10" class="blob-code blob-code-inner js-file-line"><span class="pl-c"><span class="pl-c">#</span> Make uv readable and executable by all users</span></td>
        </tr>
        <tr>
          <td id="file-dockerfile-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-dockerfile-LC11" class="blob-code blob-code-inner js-file-line"><span class="pl-k">RUN</span> install -m 0755 /root/.local/bin/uv  /usr/local/bin/uv \</td>
        </tr>
        <tr>
          <td id="file-dockerfile-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-dockerfile-LC12" class="blob-code blob-code-inner js-file-line">    &amp;&amp; install -m 0755 /root/.local/bin/uvx /usr/local/bin/uvx</td>
        </tr>
        <tr>
          <td id="file-dockerfile-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-dockerfile-LC13" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-dockerfile-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-dockerfile-LC14" class="blob-code blob-code-inner js-file-line"><span class="pl-c"><span class="pl-c">#</span> Put uv/uvx in PATH</span></td>
        </tr>
        <tr>
          <td id="file-dockerfile-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-dockerfile-LC15" class="blob-code blob-code-inner js-file-line"><span class="pl-k">ENV</span> PATH=<span class="pl-s">"/root/.local/bin:${PATH}"</span></td>
        </tr>
        <tr>
          <td id="file-dockerfile-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-dockerfile-LC16" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-dockerfile-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-dockerfile-LC17" class="blob-code blob-code-inner js-file-line"><span class="pl-c"><span class="pl-c">#</span> Verify uv installation</span></td>
        </tr>
        <tr>
          <td id="file-dockerfile-L18" class="blob-num js-line-number js-blob-rnum" data-line-number="18"></td>
          <td id="file-dockerfile-LC18" class="blob-code blob-code-inner js-file-line"><span class="pl-k">RUN</span> uvx --version</td>
        </tr>
        <tr>
          <td id="file-dockerfile-L19" class="blob-num js-line-number js-blob-rnum" data-line-number="19"></td>
          <td id="file-dockerfile-LC19" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-dockerfile-L20" class="blob-num js-line-number js-blob-rnum" data-line-number="20"></td>
          <td id="file-dockerfile-LC20" class="blob-code blob-code-inner js-file-line"><span class="pl-c"><span class="pl-c">#</span> Set environment variables</span></td>
        </tr>
        <tr>
          <td id="file-dockerfile-L21" class="blob-num js-line-number js-blob-rnum" data-line-number="21"></td>
          <td id="file-dockerfile-LC21" class="blob-code blob-code-inner js-file-line"><span class="pl-k">ENV</span> PYTHONPATH=<span class="pl-s">"${LAMBDA_TASK_ROOT}"</span></td>
        </tr>
        <tr>
          <td id="file-dockerfile-L22" class="blob-num js-line-number js-blob-rnum" data-line-number="22"></td>
          <td id="file-dockerfile-LC22" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-dockerfile-L23" class="blob-num js-line-number js-blob-rnum" data-line-number="23"></td>
          <td id="file-dockerfile-LC23" class="blob-code blob-code-inner js-file-line"><span class="pl-c"><span class="pl-c">#</span> Copy requirements file</span></td>
        </tr>
        <tr>
          <td id="file-dockerfile-L24" class="blob-num js-line-number js-blob-rnum" data-line-number="24"></td>
          <td id="file-dockerfile-LC24" class="blob-code blob-code-inner js-file-line"><span class="pl-k">COPY</span> requirements.txt ${LAMBDA_TASK_ROOT}/</td>
        </tr>
        <tr>
          <td id="file-dockerfile-L25" class="blob-num js-line-number js-blob-rnum" data-line-number="25"></td>
          <td id="file-dockerfile-LC25" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-dockerfile-L26" class="blob-num js-line-number js-blob-rnum" data-line-number="26"></td>
          <td id="file-dockerfile-LC26" class="blob-code blob-code-inner js-file-line"><span class="pl-c"><span class="pl-c">#</span> Install Python dependencies</span></td>
        </tr>
        <tr>
          <td id="file-dockerfile-L27" class="blob-num js-line-number js-blob-rnum" data-line-number="27"></td>
          <td id="file-dockerfile-LC27" class="blob-code blob-code-inner js-file-line"><span class="pl-k">RUN</span> pip install --no-cache-dir -r requirements.txt \</td>
        </tr>
        <tr>
          <td id="file-dockerfile-L28" class="blob-num js-line-number js-blob-rnum" data-line-number="28"></td>
          <td id="file-dockerfile-LC28" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"><span class="pl-c">#</span> Clean up to reduce image size</span></td>
        </tr>
        <tr>
          <td id="file-dockerfile-L29" class="blob-num js-line-number js-blob-rnum" data-line-number="29"></td>
          <td id="file-dockerfile-LC29" class="blob-code blob-code-inner js-file-line">    &amp;&amp; find /var/lang/lib/python3.12/site-packages -name <span class="pl-s">"*.pyc"</span> -delete \</td>
        </tr>
        <tr>
          <td id="file-dockerfile-L30" class="blob-num js-line-number js-blob-rnum" data-line-number="30"></td>
          <td id="file-dockerfile-LC30" class="blob-code blob-code-inner js-file-line">    &amp;&amp; find /var/lang/lib/python3.12/site-packages -name <span class="pl-s">"__pycache__"</span> -exec rm -rf {} + 2&gt;/dev/null || true \</td>
        </tr>
        <tr>
          <td id="file-dockerfile-L31" class="blob-num js-line-number js-blob-rnum" data-line-number="31"></td>
          <td id="file-dockerfile-LC31" class="blob-code blob-code-inner js-file-line">    &amp;&amp; find /var/lang/lib/python3.12/site-packages -name <span class="pl-s">"test*"</span> -exec rm -rf {} + 2&gt;/dev/null || true</td>
        </tr>
        <tr>
          <td id="file-dockerfile-L32" class="blob-num js-line-number js-blob-rnum" data-line-number="32"></td>
          <td id="file-dockerfile-LC32" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-dockerfile-L33" class="blob-num js-line-number js-blob-rnum" data-line-number="33"></td>
          <td id="file-dockerfile-LC33" class="blob-code blob-code-inner js-file-line"><span class="pl-c"><span class="pl-c">#</span> Copy function code</span></td>
        </tr>
        <tr>
          <td id="file-dockerfile-L34" class="blob-num js-line-number js-blob-rnum" data-line-number="34"></td>
          <td id="file-dockerfile-LC34" class="blob-code blob-code-inner js-file-line"><span class="pl-k">COPY</span> src/*.py ${LAMBDA_TASK_ROOT}/</td>
        </tr>
        <tr>
          <td id="file-dockerfile-L35" class="blob-num js-line-number js-blob-rnum" data-line-number="35"></td>
          <td id="file-dockerfile-LC35" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-dockerfile-L36" class="blob-num js-line-number js-blob-rnum" data-line-number="36"></td>
          <td id="file-dockerfile-LC36" class="blob-code blob-code-inner js-file-line"><span class="pl-c"><span class="pl-c">#</span> Copy PagerDuty MCP server and pre-install dependencies</span></td>
        </tr>
        <tr>
          <td id="file-dockerfile-L37" class="blob-num js-line-number js-blob-rnum" data-line-number="37"></td>
          <td id="file-dockerfile-LC37" class="blob-code blob-code-inner js-file-line"><span class="pl-k">COPY</span> pagerduty-mcp-server /opt/pagerduty-mcp-server</td>
        </tr>
        <tr>
          <td id="file-dockerfile-L38" class="blob-num js-line-number js-blob-rnum" data-line-number="38"></td>
          <td id="file-dockerfile-LC38" class="blob-code blob-code-inner js-file-line"><span class="pl-k">RUN</span> cd /opt/pagerduty-mcp-server &amp;&amp; \</td>
        </tr>
        <tr>
          <td id="file-dockerfile-L39" class="blob-num js-line-number js-blob-rnum" data-line-number="39"></td>
          <td id="file-dockerfile-LC39" class="blob-code blob-code-inner js-file-line">    uv sync --frozen &amp;&amp; \</td>
        </tr>
        <tr>
          <td id="file-dockerfile-L40" class="blob-num js-line-number js-blob-rnum" data-line-number="40"></td>
          <td id="file-dockerfile-LC40" class="blob-code blob-code-inner js-file-line">    chmod -R a+rX /opt/pagerduty-mcp-server</td>
        </tr>
        <tr>
          <td id="file-dockerfile-L41" class="blob-num js-line-number js-blob-rnum" data-line-number="41"></td>
          <td id="file-dockerfile-LC41" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-dockerfile-L42" class="blob-num js-line-number js-blob-rnum" data-line-number="42"></td>
          <td id="file-dockerfile-LC42" class="blob-code blob-code-inner js-file-line"><span class="pl-c"><span class="pl-c">#</span> Set the CMD to your handler</span></td>
        </tr>
        <tr>
          <td id="file-dockerfile-L43" class="blob-num js-line-number js-blob-rnum" data-line-number="43"></td>
          <td id="file-dockerfile-LC43" class="blob-code blob-code-inner js-file-line"><span class="pl-k">CMD</span> [<span class="pl-s">"worker.lambda_handler"</span>]</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/4385738ed45b998e71b3da16a73e4e55/raw/3a0399350d3c74b73ad871c0a93bd929cb53bddc/Dockerfile" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/4385738ed45b998e71b3da16a73e4e55#file-dockerfile" class="Link--inTextBlock">
          Dockerfile
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>The container approach gives us a few key advantages. First, we can pre-install and configure the MCP servers during the build process. Notice how we&#8217;re copying the PagerDuty MCP server into `/opt/` and running `uv sync` to install its dependencies. This happens at build time, not runtime.</p><p>Second, we get much more predictable dependency resolution. The `uv` package manager handles all the complex dependency trees and gives us reproducible builds. No more &#8220;it works on my machine&#8221; issues when deploying to Lambda.</p><p>Third, the container image becomes our deployment artifact. Terraform can build, tag, and push the image to ECR, then update the Lambda function to use the new image - all in one deployment cycle. No more managing separate layer deployments or worrying about version mismatches.</p><p>The performance impact has been minimal. Cold starts are slightly longer due to the larger image size, but the warm execution performance is actually better because all dependencies are pre-resolved and ready to go. For a Slack bot that processes messages intermittently, this trade-off makes perfect sense.</p><p><strong>Code Organization: Breaking Up the Monolith</strong></p><p>One thing I learned from the original Vera implementation was that shoving everything into a single Python file becomes a maintenance nightmare pretty quickly. The first version worked fine when it was just basic AWS Bedrock calls and Slack responses, but as soon as I started adding MCP integrations and agentic capabilities, that single file ballooned to over 800 lines.</p><p>This time around, I split things up from the start. The worker Lambda is now organized into focused modules, each handling a specific aspect of the bot&#8217;s functionality:</p><ul><li><p>worker.py - Main Lambda handler and Slack app registration</p></li><li><p>worker_inputs.py - Configuration, constants, and system prompts</p></li><li><p>worker_agent.py - Strands agent setup and execution</p></li><li><p>worker_aws.py - AWS service integrations (Bedrock, Secrets Manager)</p></li><li><p>worker_slack.py - Slack-specific utilities and response formatting</p></li><li><p>worker_conversation.py - Message processing and conversation flow</p></li><li><p>worker_lambda.py - Lambda-specific utilities and event handling</p></li><li><p>worker_mcp_*.py - Individual MCP client implementations</p></li></ul><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist141197008\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-worker_imports-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;worker_imports.py content, created by KyMidd on 07:33PM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;4\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;worker_imports.py\&quot;>\n        <tr>\n          <td id=\&quot;file-worker_imports-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-worker_imports-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c># Global imports</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_imports-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-worker_imports-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>import</span> <span class=pl-s1>os</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_imports-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-worker_imports-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>import</span> <span class=pl-s1>json</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_imports-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-worker_imports-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_imports-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-worker_imports-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c># Slack app imports</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_imports-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-worker_imports-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>from</span> <span class=pl-s1>slack_bolt</span>.<span class=pl-s1>adapter</span>.<span class=pl-s1>aws_lambda</span> <span class=pl-k>import</span> <span class=pl-v>SlackRequestHandler</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_imports-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-worker_imports-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_imports-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-worker_imports-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c># Import all constants and configuration</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_imports-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-worker_imports-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>from</span> <span class=pl-s1>worker_inputs</span> <span class=pl-k>import</span> <span class=pl-c1>*</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_imports-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-worker_imports-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_imports-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-worker_imports-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c>###</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_imports-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-worker_imports-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c># Local imports</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_imports-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-worker_imports-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c>###</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_imports-py-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-worker_imports-py-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_imports-py-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-worker_imports-py-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>from</span> <span class=pl-s1>worker_slack</span> <span class=pl-k>import</span> <span class=pl-s1>update_slack_response</span>, <span class=pl-s1>register_slack_app</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_imports-py-L16\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;16\&quot;></td>\n          <td id=\&quot;file-worker_imports-py-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>from</span> <span class=pl-s1>worker_aws</span> <span class=pl-k>import</span> (</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_imports-py-L17\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;17\&quot;></td>\n          <td id=\&quot;file-worker_imports-py-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>get_secret_with_client</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_imports-py-L18\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;18\&quot;></td>\n          <td id=\&quot;file-worker_imports-py-LC18\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>create_bedrock_client</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_imports-py-L19\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;19\&quot;></td>\n          <td id=\&quot;file-worker_imports-py-LC19\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>ai_request</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_imports-py-L20\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;20\&quot;></td>\n          <td id=\&quot;file-worker_imports-py-LC20\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>enrich_guardrail_block</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_imports-py-L21\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;21\&quot;></td>\n          <td id=\&quot;file-worker_imports-py-LC21\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_imports-py-L22\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;22\&quot;></td>\n          <td id=\&quot;file-worker_imports-py-LC22\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>from</span> <span class=pl-s1>worker_agent</span> <span class=pl-k>import</span> <span class=pl-s1>execute_agent</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_imports-py-L23\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;23\&quot;></td>\n          <td id=\&quot;file-worker_imports-py-LC23\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>from</span> <span class=pl-s1>worker_conversation</span> <span class=pl-k>import</span> <span class=pl-s1>build_conversation_content</span>, <span class=pl-s1>handle_message_event</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_imports-py-L24\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;24\&quot;></td>\n          <td id=\&quot;file-worker_imports-py-LC24\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>from</span> <span class=pl-s1>worker_lambda</span> <span class=pl-k>import</span> <span class=pl-s1>isolate_event_body</span>, <span class=pl-s1>generate_response</span></td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/d8525d3e84eba78f26577b9c2caa42e5/raw/1c0033ed2e6865ae20818b50dbde26d054357c01/worker_imports.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/d8525d3e84eba78f26577b9c2caa42e5#file-worker_imports-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          worker_imports.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-59543e005c9c.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-59543e005c9c.css"><div id="gist141197008" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-worker_imports-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="4" data-paste-markdown-skip="" data-tagsearch-path="worker_imports.py">
        <tbody><tr>
          <td id="file-worker_imports-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-worker_imports-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-c"># Global imports</span></td>
        </tr>
        <tr>
          <td id="file-worker_imports-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-worker_imports-py-LC2" class="blob-code blob-code-inner js-file-line"><span class="pl-k">import</span> <span class="pl-s1">os</span></td>
        </tr>
        <tr>
          <td id="file-worker_imports-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-worker_imports-py-LC3" class="blob-code blob-code-inner js-file-line"><span class="pl-k">import</span> <span class="pl-s1">json</span></td>
        </tr>
        <tr>
          <td id="file-worker_imports-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-worker_imports-py-LC4" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-worker_imports-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-worker_imports-py-LC5" class="blob-code blob-code-inner js-file-line"><span class="pl-c"># Slack app imports</span></td>
        </tr>
        <tr>
          <td id="file-worker_imports-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-worker_imports-py-LC6" class="blob-code blob-code-inner js-file-line"><span class="pl-k">from</span> <span class="pl-s1">slack_bolt</span>.<span class="pl-s1">adapter</span>.<span class="pl-s1">aws_lambda</span> <span class="pl-k">import</span> <span class="pl-v">SlackRequestHandler</span></td>
        </tr>
        <tr>
          <td id="file-worker_imports-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-worker_imports-py-LC7" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-worker_imports-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-worker_imports-py-LC8" class="blob-code blob-code-inner js-file-line"><span class="pl-c"># Import all constants and configuration</span></td>
        </tr>
        <tr>
          <td id="file-worker_imports-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-worker_imports-py-LC9" class="blob-code blob-code-inner js-file-line"><span class="pl-k">from</span> <span class="pl-s1">worker_inputs</span> <span class="pl-k">import</span> <span class="pl-c1">*</span></td>
        </tr>
        <tr>
          <td id="file-worker_imports-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-worker_imports-py-LC10" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-worker_imports-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-worker_imports-py-LC11" class="blob-code blob-code-inner js-file-line"><span class="pl-c">###</span></td>
        </tr>
        <tr>
          <td id="file-worker_imports-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-worker_imports-py-LC12" class="blob-code blob-code-inner js-file-line"><span class="pl-c"># Local imports</span></td>
        </tr>
        <tr>
          <td id="file-worker_imports-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-worker_imports-py-LC13" class="blob-code blob-code-inner js-file-line"><span class="pl-c">###</span></td>
        </tr>
        <tr>
          <td id="file-worker_imports-py-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-worker_imports-py-LC14" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-worker_imports-py-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-worker_imports-py-LC15" class="blob-code blob-code-inner js-file-line"><span class="pl-k">from</span> <span class="pl-s1">worker_slack</span> <span class="pl-k">import</span> <span class="pl-s1">update_slack_response</span>, <span class="pl-s1">register_slack_app</span></td>
        </tr>
        <tr>
          <td id="file-worker_imports-py-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-worker_imports-py-LC16" class="blob-code blob-code-inner js-file-line"><span class="pl-k">from</span> <span class="pl-s1">worker_aws</span> <span class="pl-k">import</span> (</td>
        </tr>
        <tr>
          <td id="file-worker_imports-py-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-worker_imports-py-LC17" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">get_secret_with_client</span>,</td>
        </tr>
        <tr>
          <td id="file-worker_imports-py-L18" class="blob-num js-line-number js-blob-rnum" data-line-number="18"></td>
          <td id="file-worker_imports-py-LC18" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">create_bedrock_client</span>,</td>
        </tr>
        <tr>
          <td id="file-worker_imports-py-L19" class="blob-num js-line-number js-blob-rnum" data-line-number="19"></td>
          <td id="file-worker_imports-py-LC19" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">ai_request</span>,</td>
        </tr>
        <tr>
          <td id="file-worker_imports-py-L20" class="blob-num js-line-number js-blob-rnum" data-line-number="20"></td>
          <td id="file-worker_imports-py-LC20" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">enrich_guardrail_block</span>,</td>
        </tr>
        <tr>
          <td id="file-worker_imports-py-L21" class="blob-num js-line-number js-blob-rnum" data-line-number="21"></td>
          <td id="file-worker_imports-py-LC21" class="blob-code blob-code-inner js-file-line">)</td>
        </tr>
        <tr>
          <td id="file-worker_imports-py-L22" class="blob-num js-line-number js-blob-rnum" data-line-number="22"></td>
          <td id="file-worker_imports-py-LC22" class="blob-code blob-code-inner js-file-line"><span class="pl-k">from</span> <span class="pl-s1">worker_agent</span> <span class="pl-k">import</span> <span class="pl-s1">execute_agent</span></td>
        </tr>
        <tr>
          <td id="file-worker_imports-py-L23" class="blob-num js-line-number js-blob-rnum" data-line-number="23"></td>
          <td id="file-worker_imports-py-LC23" class="blob-code blob-code-inner js-file-line"><span class="pl-k">from</span> <span class="pl-s1">worker_conversation</span> <span class="pl-k">import</span> <span class="pl-s1">build_conversation_content</span>, <span class="pl-s1">handle_message_event</span></td>
        </tr>
        <tr>
          <td id="file-worker_imports-py-L24" class="blob-num js-line-number js-blob-rnum" data-line-number="24"></td>
          <td id="file-worker_imports-py-LC24" class="blob-code blob-code-inner js-file-line"><span class="pl-k">from</span> <span class="pl-s1">worker_lambda</span> <span class="pl-k">import</span> <span class="pl-s1">isolate_event_body</span>, <span class="pl-s1">generate_response</span></td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/d8525d3e84eba78f26577b9c2caa42e5/raw/1c0033ed2e6865ae20818b50dbde26d054357c01/worker_imports.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/d8525d3e84eba78f26577b9c2caa42e5#file-worker_imports-py" class="Link--inTextBlock">
          worker_imports.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>The import structure shows how clean this separation becomes. Each module has a single responsibility, which makes testing and debugging so much easier. When something goes wrong with PagerDuty integration, I know exactly where to look. When I need to adjust the system prompt or brand voice, it&#8217;s all in `worker_inputs.py`.</p><p>What&#8217;s particularly nice about this approach is how it handles the MCP integrations. Each service gets its own module (`worker_mcp_pagerduty.py`, `worker_mcp_github.py`, etc.) that encapsulates all the client setup, authentication, and tool registration logic. The main agent module just imports what it needs and doesn&#8217;t have to worry about the implementation details.</p><p>This modular structure also makes the container build more efficient. Docker can cache layers better when dependencies are clearly separated, and the overall codebase becomes much more approachable for anyone who needs to maintain or extend it later.</p><h1><strong>MCP Protocol Integrations: Connecting External Tools</strong></h1><p>Here&#8217;s where things get really interesting. The Model Context Protocol is what transforms this from just another chatbot into an actual agentic system that can take actions across your toolchain. Instead of the AI just talking about your GitHub issues or PagerDuty incidents, it can actually query them directly and give you real-time information.</p><p>The beauty of MCP is that it provides a standardized way for AI models to interact with external systems. Each service exposes its capabilities as &#8220;tools&#8221; that the AI can discover and use dynamically. When someone asks &#8220;what incidents are assigned to me?&#8221; the bot doesn&#8217;t just guess - it calls the PagerDuty API through the MCP interface and gets the actual data.</p><blockquote><p>I filter all tools in their own file, so I can only return tools to the agent that can do READ behavior (non-modify). AIs are way too scary to give them write tools, IMO. You can say read-only = False if you want all the Write tools also. </p></blockquote><h2><strong>PagerDuty Integration</strong></h2><p>The PagerDuty integration handles incident management and on-call scheduling. The MCP server runs as a pre-installed component in the container, which means zero cold-start overhead when the bot needs to query PagerDuty.</p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist141197091\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-worker_mcp_pagerduty-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;worker_mcp_pagerduty.py content, created by KyMidd on 07:36PM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;4\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;worker_mcp_pagerduty.py\&quot;>\n        <tr>\n          <td id=\&quot;file-worker_mcp_pagerduty-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-worker_mcp_pagerduty-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-en>build_pagerduty_mcp_client</span>(<span class=pl-s1>pagerduty_api_key</span>, <span class=pl-s1>pagerduty_api_url</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_pagerduty-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-worker_mcp_pagerduty-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s>&amp;quot;&amp;quot;&amp;quot;Build PagerDuty MCP client with available tools&amp;quot;&amp;quot;&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_pagerduty-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-worker_mcp_pagerduty-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_pagerduty-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-worker_mcp_pagerduty-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Build the PagerDuty MCP client</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_pagerduty-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-worker_mcp_pagerduty-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>pagerduty_mcp_client</span> <span class=pl-c1>=</span> <span class=pl-en>MCPClient</span>(</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_pagerduty-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-worker_mcp_pagerduty-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>server_path</span><span class=pl-c1>=</span><span class=pl-s>&amp;quot;/opt/pagerduty-mcp-server&amp;quot;</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_pagerduty-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-worker_mcp_pagerduty-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>server_args</span><span class=pl-c1>=</span>[],</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_pagerduty-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-worker_mcp_pagerduty-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>server_env</span><span class=pl-c1>=</span>{</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_pagerduty-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-worker_mcp_pagerduty-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s>&amp;quot;PAGERDUTY_API_KEY&amp;quot;</span>: <span class=pl-s1>pagerduty_api_key</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_pagerduty-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-worker_mcp_pagerduty-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s>&amp;quot;PAGERDUTY_API_URL&amp;quot;</span>: <span class=pl-s1>pagerduty_api_url</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_pagerduty-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-worker_mcp_pagerduty-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        },</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_pagerduty-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-worker_mcp_pagerduty-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    )</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_pagerduty-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-worker_mcp_pagerduty-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_pagerduty-py-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-worker_mcp_pagerduty-py-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># List available tools from the PagerDuty MCP server</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_pagerduty-py-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-worker_mcp_pagerduty-py-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>available_tools</span> <span class=pl-c1>=</span> <span class=pl-s1>pagerduty_mcp_client</span>.<span class=pl-c1>list_tools</span>()</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_pagerduty-py-L16\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;16\&quot;></td>\n          <td id=\&quot;file-worker_mcp_pagerduty-py-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-en>print</span>(<span class=pl-s>f&amp;quot;&#128993; Available PagerDuty MCP tools: <span class=pl-s1><span class=pl-kos>{</span>[<span class=pl-s1>tool</span>.<span class=pl-c1>name</span> <span class=pl-k>for</span> <span class=pl-s1>tool</span> <span class=pl-c1>in</span> <span class=pl-s1>available_tools</span>]<span class=pl-kos>}</span></span>&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_pagerduty-py-L17\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;17\&quot;></td>\n          <td id=\&quot;file-worker_mcp_pagerduty-py-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_pagerduty-py-L18\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;18\&quot;></td>\n          <td id=\&quot;file-worker_mcp_pagerduty-py-LC18\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Convert MCP tools to Strands-compatible tools</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_pagerduty-py-L19\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;19\&quot;></td>\n          <td id=\&quot;file-worker_mcp_pagerduty-py-LC19\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>pagerduty_tools</span> <span class=pl-c1>=</span> [</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_pagerduty-py-L20\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;20\&quot;></td>\n          <td id=\&quot;file-worker_mcp_pagerduty-py-LC20\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>pagerduty_mcp_client</span>.<span class=pl-c1>create_tool</span>(<span class=pl-s1>tool</span>) <span class=pl-k>for</span> <span class=pl-s1>tool</span> <span class=pl-c1>in</span> <span class=pl-s1>available_tools</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_pagerduty-py-L21\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;21\&quot;></td>\n          <td id=\&quot;file-worker_mcp_pagerduty-py-LC21\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    ]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_pagerduty-py-L22\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;22\&quot;></td>\n          <td id=\&quot;file-worker_mcp_pagerduty-py-LC22\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_pagerduty-py-L23\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;23\&quot;></td>\n          <td id=\&quot;file-worker_mcp_pagerduty-py-LC23\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>return</span> <span class=pl-s1>pagerduty_mcp_client</span>, <span class=pl-s1>pagerduty_tools</span></td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/e135373390bac5aae8147d8d47ef298d/raw/c21813f9d9aad47504e1372492fc40e8c5a9bd9a/worker_mcp_pagerduty.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/e135373390bac5aae8147d8d47ef298d#file-worker_mcp_pagerduty-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          worker_mcp_pagerduty.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-59543e005c9c.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-59543e005c9c.css"><div id="gist141197091" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-worker_mcp_pagerduty-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="4" data-paste-markdown-skip="" data-tagsearch-path="worker_mcp_pagerduty.py">
        <tbody><tr>
          <td id="file-worker_mcp_pagerduty-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-worker_mcp_pagerduty-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-en">build_pagerduty_mcp_client</span>(<span class="pl-s1">pagerduty_api_key</span>, <span class="pl-s1">pagerduty_api_url</span>):</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_pagerduty-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-worker_mcp_pagerduty-py-LC2" class="blob-code blob-code-inner js-file-line">    <span class="pl-s">"""Build PagerDuty MCP client with available tools"""</span></td>
        </tr>
        <tr>
          <td id="file-worker_mcp_pagerduty-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-worker_mcp_pagerduty-py-LC3" class="blob-code blob-code-inner js-file-line">    </td>
        </tr>
        <tr>
          <td id="file-worker_mcp_pagerduty-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-worker_mcp_pagerduty-py-LC4" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Build the PagerDuty MCP client</span></td>
        </tr>
        <tr>
          <td id="file-worker_mcp_pagerduty-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-worker_mcp_pagerduty-py-LC5" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">pagerduty_mcp_client</span> <span class="pl-c1">=</span> <span class="pl-en">MCPClient</span>(</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_pagerduty-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-worker_mcp_pagerduty-py-LC6" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">server_path</span><span class="pl-c1">=</span><span class="pl-s">"/opt/pagerduty-mcp-server"</span>,</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_pagerduty-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-worker_mcp_pagerduty-py-LC7" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">server_args</span><span class="pl-c1">=</span>[],</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_pagerduty-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-worker_mcp_pagerduty-py-LC8" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">server_env</span><span class="pl-c1">=</span>{</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_pagerduty-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-worker_mcp_pagerduty-py-LC9" class="blob-code blob-code-inner js-file-line">            <span class="pl-s">"PAGERDUTY_API_KEY"</span>: <span class="pl-s1">pagerduty_api_key</span>,</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_pagerduty-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-worker_mcp_pagerduty-py-LC10" class="blob-code blob-code-inner js-file-line">            <span class="pl-s">"PAGERDUTY_API_URL"</span>: <span class="pl-s1">pagerduty_api_url</span>,</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_pagerduty-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-worker_mcp_pagerduty-py-LC11" class="blob-code blob-code-inner js-file-line">        },</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_pagerduty-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-worker_mcp_pagerduty-py-LC12" class="blob-code blob-code-inner js-file-line">    )</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_pagerduty-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-worker_mcp_pagerduty-py-LC13" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_pagerduty-py-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-worker_mcp_pagerduty-py-LC14" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># List available tools from the PagerDuty MCP server</span></td>
        </tr>
        <tr>
          <td id="file-worker_mcp_pagerduty-py-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-worker_mcp_pagerduty-py-LC15" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">available_tools</span> <span class="pl-c1">=</span> <span class="pl-s1">pagerduty_mcp_client</span>.<span class="pl-c1">list_tools</span>()</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_pagerduty-py-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-worker_mcp_pagerduty-py-LC16" class="blob-code blob-code-inner js-file-line">    <span class="pl-en">print</span>(<span class="pl-s">f"&#128993; Available PagerDuty MCP tools: <span class="pl-s1"><span class="pl-kos">{</span>[<span class="pl-s1">tool</span>.<span class="pl-c1">name</span> <span class="pl-k">for</span> <span class="pl-s1">tool</span> <span class="pl-c1">in</span> <span class="pl-s1">available_tools</span>]<span class="pl-kos">}</span></span>"</span>)</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_pagerduty-py-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-worker_mcp_pagerduty-py-LC17" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_pagerduty-py-L18" class="blob-num js-line-number js-blob-rnum" data-line-number="18"></td>
          <td id="file-worker_mcp_pagerduty-py-LC18" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Convert MCP tools to Strands-compatible tools</span></td>
        </tr>
        <tr>
          <td id="file-worker_mcp_pagerduty-py-L19" class="blob-num js-line-number js-blob-rnum" data-line-number="19"></td>
          <td id="file-worker_mcp_pagerduty-py-LC19" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">pagerduty_tools</span> <span class="pl-c1">=</span> [</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_pagerduty-py-L20" class="blob-num js-line-number js-blob-rnum" data-line-number="20"></td>
          <td id="file-worker_mcp_pagerduty-py-LC20" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">pagerduty_mcp_client</span>.<span class="pl-c1">create_tool</span>(<span class="pl-s1">tool</span>) <span class="pl-k">for</span> <span class="pl-s1">tool</span> <span class="pl-c1">in</span> <span class="pl-s1">available_tools</span></td>
        </tr>
        <tr>
          <td id="file-worker_mcp_pagerduty-py-L21" class="blob-num js-line-number js-blob-rnum" data-line-number="21"></td>
          <td id="file-worker_mcp_pagerduty-py-LC21" class="blob-code blob-code-inner js-file-line">    ]</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_pagerduty-py-L22" class="blob-num js-line-number js-blob-rnum" data-line-number="22"></td>
          <td id="file-worker_mcp_pagerduty-py-LC22" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_pagerduty-py-L23" class="blob-num js-line-number js-blob-rnum" data-line-number="23"></td>
          <td id="file-worker_mcp_pagerduty-py-LC23" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">return</span> <span class="pl-s1">pagerduty_mcp_client</span>, <span class="pl-s1">pagerduty_tools</span></td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/e135373390bac5aae8147d8d47ef298d/raw/c21813f9d9aad47504e1372492fc40e8c5a9bd9a/worker_mcp_pagerduty.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/e135373390bac5aae8147d8d47ef298d#file-worker_mcp_pagerduty-py" class="Link--inTextBlock">
          worker_mcp_pagerduty.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>The setup is straightforward - we pass the API credentials through environment variables and let the MCP client discover what tools are available. The PagerDuty MCP server typically exposes tools for listing incidents, getting on-call schedules, and querying service status.</p><h2><strong>GitHub Integration</strong></h2><p>GitHub integration is particularly useful for development teams. The bot can pull pull request status, check repository information, and even search across codebases. I&#8217;ve implemented it with read-only access by default because, let&#8217;s be honest, you probably don&#8217;t want your Slack bot accidentally merging pull requests.</p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist141197096\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-worker_mcp_github-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;worker_mcp_github.py content, created by KyMidd on 07:37PM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;4\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;worker_mcp_github.py\&quot;>\n        <tr>\n          <td id=\&quot;file-worker_mcp_github-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-worker_mcp_github-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-en>build_github_mcp_client</span>(<span class=pl-s1>github_token</span>, <span class=pl-s1>access_level</span><span class=pl-c1>=</span><span class=pl-s>&amp;quot;read_only&amp;quot;</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_github-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-worker_mcp_github-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s>&amp;quot;&amp;quot;&amp;quot;Build GitHub MCP client with specified access level&amp;quot;&amp;quot;&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_github-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-worker_mcp_github-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_github-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-worker_mcp_github-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Set up environment for GitHub MCP</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_github-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-worker_mcp_github-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>github_env</span> <span class=pl-c1>=</span> {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_github-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-worker_mcp_github-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s>&amp;quot;GITHUB_TOKEN&amp;quot;</span>: <span class=pl-s1>github_token</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_github-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-worker_mcp_github-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    }</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_github-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-worker_mcp_github-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_github-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-worker_mcp_github-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Build the GitHub MCP client using streamable HTTP client</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_github-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-worker_mcp_github-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>github_mcp_client</span> <span class=pl-c1>=</span> <span class=pl-en>MCPClient</span>(</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_github-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-worker_mcp_github-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>server_factory</span><span class=pl-c1>=</span><span class=pl-k>lambda</span>: <span class=pl-en>streamablehttp_client</span>(<span class=pl-s>&amp;quot;http://localhost:8000/mcp&amp;quot;</span>),</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_github-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-worker_mcp_github-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>server_env</span><span class=pl-c1>=</span><span class=pl-s1>github_env</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_github-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-worker_mcp_github-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    )</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_github-py-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-worker_mcp_github-py-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_github-py-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-worker_mcp_github-py-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># List available tools from GitHub MCP</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_github-py-L16\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;16\&quot;></td>\n          <td id=\&quot;file-worker_mcp_github-py-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>available_tools</span> <span class=pl-c1>=</span> <span class=pl-s1>github_mcp_client</span>.<span class=pl-c1>list_tools</span>()</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_github-py-L17\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;17\&quot;></td>\n          <td id=\&quot;file-worker_mcp_github-py-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-en>print</span>(<span class=pl-s>f&amp;quot;&#128993; Available GitHub MCP tools: <span class=pl-s1><span class=pl-kos>{</span>[<span class=pl-s1>tool</span>.<span class=pl-c1>name</span> <span class=pl-k>for</span> <span class=pl-s1>tool</span> <span class=pl-c1>in</span> <span class=pl-s1>available_tools</span>]<span class=pl-kos>}</span></span>&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_github-py-L18\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;18\&quot;></td>\n          <td id=\&quot;file-worker_mcp_github-py-LC18\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_github-py-L19\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;19\&quot;></td>\n          <td id=\&quot;file-worker_mcp_github-py-LC19\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Filter tools based on access level</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_github-py-L20\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;20\&quot;></td>\n          <td id=\&quot;file-worker_mcp_github-py-LC20\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>if</span> <span class=pl-s1>access_level</span> <span class=pl-c1>==</span> <span class=pl-s>&amp;quot;read_only&amp;quot;</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_github-py-L21\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;21\&quot;></td>\n          <td id=\&quot;file-worker_mcp_github-py-LC21\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># Only include read-only operations</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_github-py-L22\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;22\&quot;></td>\n          <td id=\&quot;file-worker_mcp_github-py-LC22\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>read_only_patterns</span> <span class=pl-c1>=</span> [</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_github-py-L23\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;23\&quot;></td>\n          <td id=\&quot;file-worker_mcp_github-py-LC23\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s>&amp;quot;get_&amp;quot;</span>, <span class=pl-s>&amp;quot;list_&amp;quot;</span>, <span class=pl-s>&amp;quot;search_&amp;quot;</span>, <span class=pl-s>&amp;quot;download_&amp;quot;</span>, <span class=pl-s>&amp;quot;read_&amp;quot;</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_github-py-L24\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;24\&quot;></td>\n          <td id=\&quot;file-worker_mcp_github-py-LC24\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s>&amp;quot;mcp__github__get&amp;quot;</span>, <span class=pl-s>&amp;quot;mcp__github__list&amp;quot;</span>, <span class=pl-s>&amp;quot;mcp__github__search&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_github-py-L25\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;25\&quot;></td>\n          <td id=\&quot;file-worker_mcp_github-py-LC25\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        ]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_github-py-L26\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;26\&quot;></td>\n          <td id=\&quot;file-worker_mcp_github-py-LC26\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>filtered_tools</span> <span class=pl-c1>=</span> [</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_github-py-L27\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;27\&quot;></td>\n          <td id=\&quot;file-worker_mcp_github-py-LC27\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>tool</span> <span class=pl-k>for</span> <span class=pl-s1>tool</span> <span class=pl-c1>in</span> <span class=pl-s1>available_tools</span> </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_github-py-L28\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;28\&quot;></td>\n          <td id=\&quot;file-worker_mcp_github-py-LC28\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-k>if</span> <span class=pl-en>any</span>(<span class=pl-s1>pattern</span> <span class=pl-c1>in</span> <span class=pl-s1>tool</span>.<span class=pl-c1>name</span> <span class=pl-k>for</span> <span class=pl-s1>pattern</span> <span class=pl-c1>in</span> <span class=pl-s1>read_only_patterns</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_github-py-L29\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;29\&quot;></td>\n          <td id=\&quot;file-worker_mcp_github-py-LC29\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        ]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_github-py-L30\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;30\&quot;></td>\n          <td id=\&quot;file-worker_mcp_github-py-LC30\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>else</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_github-py-L31\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;31\&quot;></td>\n          <td id=\&quot;file-worker_mcp_github-py-LC31\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># Include all tools for full access</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_github-py-L32\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;32\&quot;></td>\n          <td id=\&quot;file-worker_mcp_github-py-LC32\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>filtered_tools</span> <span class=pl-c1>=</span> <span class=pl-s1>available_tools</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_github-py-L33\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;33\&quot;></td>\n          <td id=\&quot;file-worker_mcp_github-py-LC33\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_github-py-L34\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;34\&quot;></td>\n          <td id=\&quot;file-worker_mcp_github-py-LC34\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Convert to Strands-compatible tools</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_github-py-L35\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;35\&quot;></td>\n          <td id=\&quot;file-worker_mcp_github-py-LC35\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>github_tools</span> <span class=pl-c1>=</span> [</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_github-py-L36\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;36\&quot;></td>\n          <td id=\&quot;file-worker_mcp_github-py-LC36\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>github_mcp_client</span>.<span class=pl-c1>create_tool</span>(<span class=pl-s1>tool</span>) <span class=pl-k>for</span> <span class=pl-s1>tool</span> <span class=pl-c1>in</span> <span class=pl-s1>filtered_tools</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_github-py-L37\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;37\&quot;></td>\n          <td id=\&quot;file-worker_mcp_github-py-LC37\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    ]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_github-py-L38\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;38\&quot;></td>\n          <td id=\&quot;file-worker_mcp_github-py-LC38\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_github-py-L39\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;39\&quot;></td>\n          <td id=\&quot;file-worker_mcp_github-py-LC39\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-en>print</span>(<span class=pl-s>f&amp;quot;&#128994; Using <span class=pl-s1><span class=pl-kos>{</span><span class=pl-en>len</span>(<span class=pl-s1>github_tools</span>)<span class=pl-kos>}</span></span> GitHub tools in <span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>access_level</span><span class=pl-kos>}</span></span> mode&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_github-py-L40\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;40\&quot;></td>\n          <td id=\&quot;file-worker_mcp_github-py-LC40\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>return</span> <span class=pl-s1>github_mcp_client</span>, <span class=pl-s1>github_tools</span></td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/2cd6ee505b27ef60431bfa2fd4b6369f/raw/bfbcdb05463c75041d529371fe4fd913adf6c40a/worker_mcp_github.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/2cd6ee505b27ef60431bfa2fd4b6369f#file-worker_mcp_github-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          worker_mcp_github.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-59543e005c9c.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-59543e005c9c.css"><div id="gist141197096" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-worker_mcp_github-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="4" data-paste-markdown-skip="" data-tagsearch-path="worker_mcp_github.py">
        <tbody><tr>
          <td id="file-worker_mcp_github-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-worker_mcp_github-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-en">build_github_mcp_client</span>(<span class="pl-s1">github_token</span>, <span class="pl-s1">access_level</span><span class="pl-c1">=</span><span class="pl-s">"read_only"</span>):</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_github-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-worker_mcp_github-py-LC2" class="blob-code blob-code-inner js-file-line">    <span class="pl-s">"""Build GitHub MCP client with specified access level"""</span></td>
        </tr>
        <tr>
          <td id="file-worker_mcp_github-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-worker_mcp_github-py-LC3" class="blob-code blob-code-inner js-file-line">    </td>
        </tr>
        <tr>
          <td id="file-worker_mcp_github-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-worker_mcp_github-py-LC4" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Set up environment for GitHub MCP</span></td>
        </tr>
        <tr>
          <td id="file-worker_mcp_github-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-worker_mcp_github-py-LC5" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">github_env</span> <span class="pl-c1">=</span> {</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_github-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-worker_mcp_github-py-LC6" class="blob-code blob-code-inner js-file-line">        <span class="pl-s">"GITHUB_TOKEN"</span>: <span class="pl-s1">github_token</span>,</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_github-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-worker_mcp_github-py-LC7" class="blob-code blob-code-inner js-file-line">    }</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_github-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-worker_mcp_github-py-LC8" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_github-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-worker_mcp_github-py-LC9" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Build the GitHub MCP client using streamable HTTP client</span></td>
        </tr>
        <tr>
          <td id="file-worker_mcp_github-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-worker_mcp_github-py-LC10" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">github_mcp_client</span> <span class="pl-c1">=</span> <span class="pl-en">MCPClient</span>(</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_github-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-worker_mcp_github-py-LC11" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">server_factory</span><span class="pl-c1">=</span><span class="pl-k">lambda</span>: <span class="pl-en">streamablehttp_client</span>(<span class="pl-s">"http://localhost:8000/mcp"</span>),</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_github-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-worker_mcp_github-py-LC12" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">server_env</span><span class="pl-c1">=</span><span class="pl-s1">github_env</span>,</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_github-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-worker_mcp_github-py-LC13" class="blob-code blob-code-inner js-file-line">    )</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_github-py-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-worker_mcp_github-py-LC14" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_github-py-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-worker_mcp_github-py-LC15" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># List available tools from GitHub MCP</span></td>
        </tr>
        <tr>
          <td id="file-worker_mcp_github-py-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-worker_mcp_github-py-LC16" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">available_tools</span> <span class="pl-c1">=</span> <span class="pl-s1">github_mcp_client</span>.<span class="pl-c1">list_tools</span>()</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_github-py-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-worker_mcp_github-py-LC17" class="blob-code blob-code-inner js-file-line">    <span class="pl-en">print</span>(<span class="pl-s">f"&#128993; Available GitHub MCP tools: <span class="pl-s1"><span class="pl-kos">{</span>[<span class="pl-s1">tool</span>.<span class="pl-c1">name</span> <span class="pl-k">for</span> <span class="pl-s1">tool</span> <span class="pl-c1">in</span> <span class="pl-s1">available_tools</span>]<span class="pl-kos">}</span></span>"</span>)</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_github-py-L18" class="blob-num js-line-number js-blob-rnum" data-line-number="18"></td>
          <td id="file-worker_mcp_github-py-LC18" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_github-py-L19" class="blob-num js-line-number js-blob-rnum" data-line-number="19"></td>
          <td id="file-worker_mcp_github-py-LC19" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Filter tools based on access level</span></td>
        </tr>
        <tr>
          <td id="file-worker_mcp_github-py-L20" class="blob-num js-line-number js-blob-rnum" data-line-number="20"></td>
          <td id="file-worker_mcp_github-py-LC20" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">if</span> <span class="pl-s1">access_level</span> <span class="pl-c1">==</span> <span class="pl-s">"read_only"</span>:</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_github-py-L21" class="blob-num js-line-number js-blob-rnum" data-line-number="21"></td>
          <td id="file-worker_mcp_github-py-LC21" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># Only include read-only operations</span></td>
        </tr>
        <tr>
          <td id="file-worker_mcp_github-py-L22" class="blob-num js-line-number js-blob-rnum" data-line-number="22"></td>
          <td id="file-worker_mcp_github-py-LC22" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">read_only_patterns</span> <span class="pl-c1">=</span> [</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_github-py-L23" class="blob-num js-line-number js-blob-rnum" data-line-number="23"></td>
          <td id="file-worker_mcp_github-py-LC23" class="blob-code blob-code-inner js-file-line">            <span class="pl-s">"get_"</span>, <span class="pl-s">"list_"</span>, <span class="pl-s">"search_"</span>, <span class="pl-s">"download_"</span>, <span class="pl-s">"read_"</span>,</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_github-py-L24" class="blob-num js-line-number js-blob-rnum" data-line-number="24"></td>
          <td id="file-worker_mcp_github-py-LC24" class="blob-code blob-code-inner js-file-line">            <span class="pl-s">"mcp__github__get"</span>, <span class="pl-s">"mcp__github__list"</span>, <span class="pl-s">"mcp__github__search"</span></td>
        </tr>
        <tr>
          <td id="file-worker_mcp_github-py-L25" class="blob-num js-line-number js-blob-rnum" data-line-number="25"></td>
          <td id="file-worker_mcp_github-py-LC25" class="blob-code blob-code-inner js-file-line">        ]</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_github-py-L26" class="blob-num js-line-number js-blob-rnum" data-line-number="26"></td>
          <td id="file-worker_mcp_github-py-LC26" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">filtered_tools</span> <span class="pl-c1">=</span> [</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_github-py-L27" class="blob-num js-line-number js-blob-rnum" data-line-number="27"></td>
          <td id="file-worker_mcp_github-py-LC27" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">tool</span> <span class="pl-k">for</span> <span class="pl-s1">tool</span> <span class="pl-c1">in</span> <span class="pl-s1">available_tools</span> </td>
        </tr>
        <tr>
          <td id="file-worker_mcp_github-py-L28" class="blob-num js-line-number js-blob-rnum" data-line-number="28"></td>
          <td id="file-worker_mcp_github-py-LC28" class="blob-code blob-code-inner js-file-line">            <span class="pl-k">if</span> <span class="pl-en">any</span>(<span class="pl-s1">pattern</span> <span class="pl-c1">in</span> <span class="pl-s1">tool</span>.<span class="pl-c1">name</span> <span class="pl-k">for</span> <span class="pl-s1">pattern</span> <span class="pl-c1">in</span> <span class="pl-s1">read_only_patterns</span>)</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_github-py-L29" class="blob-num js-line-number js-blob-rnum" data-line-number="29"></td>
          <td id="file-worker_mcp_github-py-LC29" class="blob-code blob-code-inner js-file-line">        ]</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_github-py-L30" class="blob-num js-line-number js-blob-rnum" data-line-number="30"></td>
          <td id="file-worker_mcp_github-py-LC30" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">else</span>:</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_github-py-L31" class="blob-num js-line-number js-blob-rnum" data-line-number="31"></td>
          <td id="file-worker_mcp_github-py-LC31" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># Include all tools for full access</span></td>
        </tr>
        <tr>
          <td id="file-worker_mcp_github-py-L32" class="blob-num js-line-number js-blob-rnum" data-line-number="32"></td>
          <td id="file-worker_mcp_github-py-LC32" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">filtered_tools</span> <span class="pl-c1">=</span> <span class="pl-s1">available_tools</span></td>
        </tr>
        <tr>
          <td id="file-worker_mcp_github-py-L33" class="blob-num js-line-number js-blob-rnum" data-line-number="33"></td>
          <td id="file-worker_mcp_github-py-LC33" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_github-py-L34" class="blob-num js-line-number js-blob-rnum" data-line-number="34"></td>
          <td id="file-worker_mcp_github-py-LC34" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Convert to Strands-compatible tools</span></td>
        </tr>
        <tr>
          <td id="file-worker_mcp_github-py-L35" class="blob-num js-line-number js-blob-rnum" data-line-number="35"></td>
          <td id="file-worker_mcp_github-py-LC35" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">github_tools</span> <span class="pl-c1">=</span> [</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_github-py-L36" class="blob-num js-line-number js-blob-rnum" data-line-number="36"></td>
          <td id="file-worker_mcp_github-py-LC36" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">github_mcp_client</span>.<span class="pl-c1">create_tool</span>(<span class="pl-s1">tool</span>) <span class="pl-k">for</span> <span class="pl-s1">tool</span> <span class="pl-c1">in</span> <span class="pl-s1">filtered_tools</span></td>
        </tr>
        <tr>
          <td id="file-worker_mcp_github-py-L37" class="blob-num js-line-number js-blob-rnum" data-line-number="37"></td>
          <td id="file-worker_mcp_github-py-LC37" class="blob-code blob-code-inner js-file-line">    ]</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_github-py-L38" class="blob-num js-line-number js-blob-rnum" data-line-number="38"></td>
          <td id="file-worker_mcp_github-py-LC38" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_github-py-L39" class="blob-num js-line-number js-blob-rnum" data-line-number="39"></td>
          <td id="file-worker_mcp_github-py-LC39" class="blob-code blob-code-inner js-file-line">    <span class="pl-en">print</span>(<span class="pl-s">f"&#128994; Using <span class="pl-s1"><span class="pl-kos">{</span><span class="pl-en">len</span>(<span class="pl-s1">github_tools</span>)<span class="pl-kos">}</span></span> GitHub tools in <span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">access_level</span><span class="pl-kos">}</span></span> mode"</span>)</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_github-py-L40" class="blob-num js-line-number js-blob-rnum" data-line-number="40"></td>
          <td id="file-worker_mcp_github-py-LC40" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">return</span> <span class="pl-s1">github_mcp_client</span>, <span class="pl-s1">github_tools</span></td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/2cd6ee505b27ef60431bfa2fd4b6369f/raw/bfbcdb05463c75041d529371fe4fd913adf6c40a/worker_mcp_github.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/2cd6ee505b27ef60431bfa2fd4b6369f#file-worker_mcp_github-py" class="Link--inTextBlock">
          worker_mcp_github.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>The filtering logic here is important. By restricting to read-only operations, we get all the query capabilities without the risk of destructive actions. The bot can tell you about issues, pull requests, and repository status, but it can&#8217;t modify anything.</p><h2><strong>Atlassian Integration</strong></h2><p>The Atlassian integration covers both Jira and Confluence, which makes it incredibly powerful for teams that live in the Atlassian ecosystem. The OAuth setup is a bit more complex than the other integrations, but once it&#8217;s configured, the bot becomes a conversational interface to your entire knowledge base.</p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist141197138\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-worker_mcp_atlassian-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;worker_mcp_atlassian.py content, created by KyMidd on 07:38PM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;4\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;worker_mcp_atlassian.py\&quot;>\n        <tr>\n          <td id=\&quot;file-worker_mcp_atlassian-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-worker_mcp_atlassian-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-en>build_atlassian_mcp_client</span>(<span class=pl-s1>refresh_token</span>, <span class=pl-s1>client_id</span>, <span class=pl-s1>access_level</span><span class=pl-c1>=</span><span class=pl-s>&amp;quot;read_only&amp;quot;</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_atlassian-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-worker_mcp_atlassian-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s>&amp;quot;&amp;quot;&amp;quot;Build Atlassian MCP client with OAuth credentials&amp;quot;&amp;quot;&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_atlassian-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-worker_mcp_atlassian-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_atlassian-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-worker_mcp_atlassian-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Set up environment for Atlassian MCP</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_atlassian-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-worker_mcp_atlassian-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>atlassian_env</span> <span class=pl-c1>=</span> {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_atlassian-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-worker_mcp_atlassian-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s>&amp;quot;ATLASSIAN_REFRESH_TOKEN&amp;quot;</span>: <span class=pl-s1>refresh_token</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_atlassian-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-worker_mcp_atlassian-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s>&amp;quot;ATLASSIAN_CLIENT_ID&amp;quot;</span>: <span class=pl-s1>client_id</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_atlassian-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-worker_mcp_atlassian-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    }</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_atlassian-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-worker_mcp_atlassian-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_atlassian-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-worker_mcp_atlassian-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Build the Atlassian MCP client</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_atlassian-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-worker_mcp_atlassian-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>atlassian_mcp_client</span> <span class=pl-c1>=</span> <span class=pl-en>MCPClient</span>(</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_atlassian-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-worker_mcp_atlassian-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>server_factory</span><span class=pl-c1>=</span><span class=pl-k>lambda</span>: <span class=pl-en>streamablehttp_client</span>(<span class=pl-s>&amp;quot;http://localhost:8001/mcp&amp;quot;</span>),</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_atlassian-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-worker_mcp_atlassian-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>server_env</span><span class=pl-c1>=</span><span class=pl-s1>atlassian_env</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_atlassian-py-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-worker_mcp_atlassian-py-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    )</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_atlassian-py-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-worker_mcp_atlassian-py-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_atlassian-py-L16\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;16\&quot;></td>\n          <td id=\&quot;file-worker_mcp_atlassian-py-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># List available tools from Atlassian MCP</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_atlassian-py-L17\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;17\&quot;></td>\n          <td id=\&quot;file-worker_mcp_atlassian-py-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>available_tools</span> <span class=pl-c1>=</span> <span class=pl-s1>atlassian_mcp_client</span>.<span class=pl-c1>list_tools</span>()</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_atlassian-py-L18\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;18\&quot;></td>\n          <td id=\&quot;file-worker_mcp_atlassian-py-LC18\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-en>print</span>(<span class=pl-s>f&amp;quot;&#128993; Available Atlassian MCP tools: <span class=pl-s1><span class=pl-kos>{</span>[<span class=pl-s1>tool</span>.<span class=pl-c1>name</span> <span class=pl-k>for</span> <span class=pl-s1>tool</span> <span class=pl-c1>in</span> <span class=pl-s1>available_tools</span>]<span class=pl-kos>}</span></span>&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_atlassian-py-L19\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;19\&quot;></td>\n          <td id=\&quot;file-worker_mcp_atlassian-py-LC19\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_atlassian-py-L20\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;20\&quot;></td>\n          <td id=\&quot;file-worker_mcp_atlassian-py-LC20\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Filter tools based on access level</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_atlassian-py-L21\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;21\&quot;></td>\n          <td id=\&quot;file-worker_mcp_atlassian-py-LC21\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>if</span> <span class=pl-s1>access_level</span> <span class=pl-c1>==</span> <span class=pl-s>&amp;quot;read_only&amp;quot;</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_atlassian-py-L22\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;22\&quot;></td>\n          <td id=\&quot;file-worker_mcp_atlassian-py-LC22\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># Only include read/search operations for Jira and Confluence</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_atlassian-py-L23\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;23\&quot;></td>\n          <td id=\&quot;file-worker_mcp_atlassian-py-LC23\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>read_only_patterns</span> <span class=pl-c1>=</span> [</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_atlassian-py-L24\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;24\&quot;></td>\n          <td id=\&quot;file-worker_mcp_atlassian-py-LC24\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s>&amp;quot;get_&amp;quot;</span>, <span class=pl-s>&amp;quot;list_&amp;quot;</span>, <span class=pl-s>&amp;quot;search_&amp;quot;</span>, <span class=pl-s>&amp;quot;read_&amp;quot;</span>, <span class=pl-s>&amp;quot;fetch_&amp;quot;</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_atlassian-py-L25\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;25\&quot;></td>\n          <td id=\&quot;file-worker_mcp_atlassian-py-LC25\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s>&amp;quot;jira_search&amp;quot;</span>, <span class=pl-s>&amp;quot;confluence_search&amp;quot;</span>, <span class=pl-s>&amp;quot;get_issue&amp;quot;</span>, <span class=pl-s>&amp;quot;get_page&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_atlassian-py-L26\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;26\&quot;></td>\n          <td id=\&quot;file-worker_mcp_atlassian-py-LC26\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        ]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_atlassian-py-L27\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;27\&quot;></td>\n          <td id=\&quot;file-worker_mcp_atlassian-py-LC27\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>filtered_tools</span> <span class=pl-c1>=</span> [</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_atlassian-py-L28\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;28\&quot;></td>\n          <td id=\&quot;file-worker_mcp_atlassian-py-LC28\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>tool</span> <span class=pl-k>for</span> <span class=pl-s1>tool</span> <span class=pl-c1>in</span> <span class=pl-s1>available_tools</span> </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_atlassian-py-L29\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;29\&quot;></td>\n          <td id=\&quot;file-worker_mcp_atlassian-py-LC29\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-k>if</span> <span class=pl-en>any</span>(<span class=pl-s1>pattern</span> <span class=pl-c1>in</span> <span class=pl-s1>tool</span>.<span class=pl-c1>name</span>.<span class=pl-c1>lower</span>() <span class=pl-k>for</span> <span class=pl-s1>pattern</span> <span class=pl-c1>in</span> <span class=pl-s1>read_only_patterns</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_atlassian-py-L30\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;30\&quot;></td>\n          <td id=\&quot;file-worker_mcp_atlassian-py-LC30\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        ]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_atlassian-py-L31\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;31\&quot;></td>\n          <td id=\&quot;file-worker_mcp_atlassian-py-LC31\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>else</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_atlassian-py-L32\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;32\&quot;></td>\n          <td id=\&quot;file-worker_mcp_atlassian-py-LC32\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># Include all tools for full access</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_atlassian-py-L33\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;33\&quot;></td>\n          <td id=\&quot;file-worker_mcp_atlassian-py-LC33\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>filtered_tools</span> <span class=pl-c1>=</span> <span class=pl-s1>available_tools</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_atlassian-py-L34\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;34\&quot;></td>\n          <td id=\&quot;file-worker_mcp_atlassian-py-LC34\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_atlassian-py-L35\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;35\&quot;></td>\n          <td id=\&quot;file-worker_mcp_atlassian-py-LC35\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Convert to Strands-compatible tools</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_atlassian-py-L36\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;36\&quot;></td>\n          <td id=\&quot;file-worker_mcp_atlassian-py-LC36\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>atlassian_tools</span> <span class=pl-c1>=</span> [</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_atlassian-py-L37\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;37\&quot;></td>\n          <td id=\&quot;file-worker_mcp_atlassian-py-LC37\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>atlassian_mcp_client</span>.<span class=pl-c1>create_tool</span>(<span class=pl-s1>tool</span>) <span class=pl-k>for</span> <span class=pl-s1>tool</span> <span class=pl-c1>in</span> <span class=pl-s1>filtered_tools</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_atlassian-py-L38\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;38\&quot;></td>\n          <td id=\&quot;file-worker_mcp_atlassian-py-LC38\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    ]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_atlassian-py-L39\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;39\&quot;></td>\n          <td id=\&quot;file-worker_mcp_atlassian-py-LC39\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_atlassian-py-L40\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;40\&quot;></td>\n          <td id=\&quot;file-worker_mcp_atlassian-py-LC40\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-en>print</span>(<span class=pl-s>f&amp;quot;&#128994; Using <span class=pl-s1><span class=pl-kos>{</span><span class=pl-en>len</span>(<span class=pl-s1>atlassian_tools</span>)<span class=pl-kos>}</span></span> Atlassian tools in <span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>access_level</span><span class=pl-kos>}</span></span> mode&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-worker_mcp_atlassian-py-L41\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;41\&quot;></td>\n          <td id=\&quot;file-worker_mcp_atlassian-py-LC41\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>return</span> <span class=pl-s1>atlassian_mcp_client</span>, <span class=pl-s1>atlassian_tools</span></td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/4264b7402423ca76d40b1aa5e3725364/raw/dbf48f1e6cb45b3d192ffb15e910e1885a28ee2f/worker_mcp_atlassian.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/4264b7402423ca76d40b1aa5e3725364#file-worker_mcp_atlassian-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          worker_mcp_atlassian.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-59543e005c9c.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-59543e005c9c.css"><div id="gist141197138" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-worker_mcp_atlassian-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="4" data-paste-markdown-skip="" data-tagsearch-path="worker_mcp_atlassian.py">
        <tbody><tr>
          <td id="file-worker_mcp_atlassian-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-worker_mcp_atlassian-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-en">build_atlassian_mcp_client</span>(<span class="pl-s1">refresh_token</span>, <span class="pl-s1">client_id</span>, <span class="pl-s1">access_level</span><span class="pl-c1">=</span><span class="pl-s">"read_only"</span>):</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_atlassian-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-worker_mcp_atlassian-py-LC2" class="blob-code blob-code-inner js-file-line">    <span class="pl-s">"""Build Atlassian MCP client with OAuth credentials"""</span></td>
        </tr>
        <tr>
          <td id="file-worker_mcp_atlassian-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-worker_mcp_atlassian-py-LC3" class="blob-code blob-code-inner js-file-line">    </td>
        </tr>
        <tr>
          <td id="file-worker_mcp_atlassian-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-worker_mcp_atlassian-py-LC4" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Set up environment for Atlassian MCP</span></td>
        </tr>
        <tr>
          <td id="file-worker_mcp_atlassian-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-worker_mcp_atlassian-py-LC5" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">atlassian_env</span> <span class="pl-c1">=</span> {</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_atlassian-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-worker_mcp_atlassian-py-LC6" class="blob-code blob-code-inner js-file-line">        <span class="pl-s">"ATLASSIAN_REFRESH_TOKEN"</span>: <span class="pl-s1">refresh_token</span>,</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_atlassian-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-worker_mcp_atlassian-py-LC7" class="blob-code blob-code-inner js-file-line">        <span class="pl-s">"ATLASSIAN_CLIENT_ID"</span>: <span class="pl-s1">client_id</span>,</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_atlassian-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-worker_mcp_atlassian-py-LC8" class="blob-code blob-code-inner js-file-line">    }</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_atlassian-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-worker_mcp_atlassian-py-LC9" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_atlassian-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-worker_mcp_atlassian-py-LC10" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Build the Atlassian MCP client</span></td>
        </tr>
        <tr>
          <td id="file-worker_mcp_atlassian-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-worker_mcp_atlassian-py-LC11" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">atlassian_mcp_client</span> <span class="pl-c1">=</span> <span class="pl-en">MCPClient</span>(</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_atlassian-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-worker_mcp_atlassian-py-LC12" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">server_factory</span><span class="pl-c1">=</span><span class="pl-k">lambda</span>: <span class="pl-en">streamablehttp_client</span>(<span class="pl-s">"http://localhost:8001/mcp"</span>),</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_atlassian-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-worker_mcp_atlassian-py-LC13" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">server_env</span><span class="pl-c1">=</span><span class="pl-s1">atlassian_env</span>,</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_atlassian-py-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-worker_mcp_atlassian-py-LC14" class="blob-code blob-code-inner js-file-line">    )</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_atlassian-py-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-worker_mcp_atlassian-py-LC15" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_atlassian-py-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-worker_mcp_atlassian-py-LC16" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># List available tools from Atlassian MCP</span></td>
        </tr>
        <tr>
          <td id="file-worker_mcp_atlassian-py-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-worker_mcp_atlassian-py-LC17" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">available_tools</span> <span class="pl-c1">=</span> <span class="pl-s1">atlassian_mcp_client</span>.<span class="pl-c1">list_tools</span>()</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_atlassian-py-L18" class="blob-num js-line-number js-blob-rnum" data-line-number="18"></td>
          <td id="file-worker_mcp_atlassian-py-LC18" class="blob-code blob-code-inner js-file-line">    <span class="pl-en">print</span>(<span class="pl-s">f"&#128993; Available Atlassian MCP tools: <span class="pl-s1"><span class="pl-kos">{</span>[<span class="pl-s1">tool</span>.<span class="pl-c1">name</span> <span class="pl-k">for</span> <span class="pl-s1">tool</span> <span class="pl-c1">in</span> <span class="pl-s1">available_tools</span>]<span class="pl-kos">}</span></span>"</span>)</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_atlassian-py-L19" class="blob-num js-line-number js-blob-rnum" data-line-number="19"></td>
          <td id="file-worker_mcp_atlassian-py-LC19" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_atlassian-py-L20" class="blob-num js-line-number js-blob-rnum" data-line-number="20"></td>
          <td id="file-worker_mcp_atlassian-py-LC20" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Filter tools based on access level</span></td>
        </tr>
        <tr>
          <td id="file-worker_mcp_atlassian-py-L21" class="blob-num js-line-number js-blob-rnum" data-line-number="21"></td>
          <td id="file-worker_mcp_atlassian-py-LC21" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">if</span> <span class="pl-s1">access_level</span> <span class="pl-c1">==</span> <span class="pl-s">"read_only"</span>:</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_atlassian-py-L22" class="blob-num js-line-number js-blob-rnum" data-line-number="22"></td>
          <td id="file-worker_mcp_atlassian-py-LC22" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># Only include read/search operations for Jira and Confluence</span></td>
        </tr>
        <tr>
          <td id="file-worker_mcp_atlassian-py-L23" class="blob-num js-line-number js-blob-rnum" data-line-number="23"></td>
          <td id="file-worker_mcp_atlassian-py-LC23" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">read_only_patterns</span> <span class="pl-c1">=</span> [</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_atlassian-py-L24" class="blob-num js-line-number js-blob-rnum" data-line-number="24"></td>
          <td id="file-worker_mcp_atlassian-py-LC24" class="blob-code blob-code-inner js-file-line">            <span class="pl-s">"get_"</span>, <span class="pl-s">"list_"</span>, <span class="pl-s">"search_"</span>, <span class="pl-s">"read_"</span>, <span class="pl-s">"fetch_"</span>,</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_atlassian-py-L25" class="blob-num js-line-number js-blob-rnum" data-line-number="25"></td>
          <td id="file-worker_mcp_atlassian-py-LC25" class="blob-code blob-code-inner js-file-line">            <span class="pl-s">"jira_search"</span>, <span class="pl-s">"confluence_search"</span>, <span class="pl-s">"get_issue"</span>, <span class="pl-s">"get_page"</span></td>
        </tr>
        <tr>
          <td id="file-worker_mcp_atlassian-py-L26" class="blob-num js-line-number js-blob-rnum" data-line-number="26"></td>
          <td id="file-worker_mcp_atlassian-py-LC26" class="blob-code blob-code-inner js-file-line">        ]</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_atlassian-py-L27" class="blob-num js-line-number js-blob-rnum" data-line-number="27"></td>
          <td id="file-worker_mcp_atlassian-py-LC27" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">filtered_tools</span> <span class="pl-c1">=</span> [</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_atlassian-py-L28" class="blob-num js-line-number js-blob-rnum" data-line-number="28"></td>
          <td id="file-worker_mcp_atlassian-py-LC28" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">tool</span> <span class="pl-k">for</span> <span class="pl-s1">tool</span> <span class="pl-c1">in</span> <span class="pl-s1">available_tools</span> </td>
        </tr>
        <tr>
          <td id="file-worker_mcp_atlassian-py-L29" class="blob-num js-line-number js-blob-rnum" data-line-number="29"></td>
          <td id="file-worker_mcp_atlassian-py-LC29" class="blob-code blob-code-inner js-file-line">            <span class="pl-k">if</span> <span class="pl-en">any</span>(<span class="pl-s1">pattern</span> <span class="pl-c1">in</span> <span class="pl-s1">tool</span>.<span class="pl-c1">name</span>.<span class="pl-c1">lower</span>() <span class="pl-k">for</span> <span class="pl-s1">pattern</span> <span class="pl-c1">in</span> <span class="pl-s1">read_only_patterns</span>)</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_atlassian-py-L30" class="blob-num js-line-number js-blob-rnum" data-line-number="30"></td>
          <td id="file-worker_mcp_atlassian-py-LC30" class="blob-code blob-code-inner js-file-line">        ]</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_atlassian-py-L31" class="blob-num js-line-number js-blob-rnum" data-line-number="31"></td>
          <td id="file-worker_mcp_atlassian-py-LC31" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">else</span>:</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_atlassian-py-L32" class="blob-num js-line-number js-blob-rnum" data-line-number="32"></td>
          <td id="file-worker_mcp_atlassian-py-LC32" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># Include all tools for full access</span></td>
        </tr>
        <tr>
          <td id="file-worker_mcp_atlassian-py-L33" class="blob-num js-line-number js-blob-rnum" data-line-number="33"></td>
          <td id="file-worker_mcp_atlassian-py-LC33" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">filtered_tools</span> <span class="pl-c1">=</span> <span class="pl-s1">available_tools</span></td>
        </tr>
        <tr>
          <td id="file-worker_mcp_atlassian-py-L34" class="blob-num js-line-number js-blob-rnum" data-line-number="34"></td>
          <td id="file-worker_mcp_atlassian-py-LC34" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_atlassian-py-L35" class="blob-num js-line-number js-blob-rnum" data-line-number="35"></td>
          <td id="file-worker_mcp_atlassian-py-LC35" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Convert to Strands-compatible tools</span></td>
        </tr>
        <tr>
          <td id="file-worker_mcp_atlassian-py-L36" class="blob-num js-line-number js-blob-rnum" data-line-number="36"></td>
          <td id="file-worker_mcp_atlassian-py-LC36" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">atlassian_tools</span> <span class="pl-c1">=</span> [</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_atlassian-py-L37" class="blob-num js-line-number js-blob-rnum" data-line-number="37"></td>
          <td id="file-worker_mcp_atlassian-py-LC37" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">atlassian_mcp_client</span>.<span class="pl-c1">create_tool</span>(<span class="pl-s1">tool</span>) <span class="pl-k">for</span> <span class="pl-s1">tool</span> <span class="pl-c1">in</span> <span class="pl-s1">filtered_tools</span></td>
        </tr>
        <tr>
          <td id="file-worker_mcp_atlassian-py-L38" class="blob-num js-line-number js-blob-rnum" data-line-number="38"></td>
          <td id="file-worker_mcp_atlassian-py-LC38" class="blob-code blob-code-inner js-file-line">    ]</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_atlassian-py-L39" class="blob-num js-line-number js-blob-rnum" data-line-number="39"></td>
          <td id="file-worker_mcp_atlassian-py-LC39" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_atlassian-py-L40" class="blob-num js-line-number js-blob-rnum" data-line-number="40"></td>
          <td id="file-worker_mcp_atlassian-py-LC40" class="blob-code blob-code-inner js-file-line">    <span class="pl-en">print</span>(<span class="pl-s">f"&#128994; Using <span class="pl-s1"><span class="pl-kos">{</span><span class="pl-en">len</span>(<span class="pl-s1">atlassian_tools</span>)<span class="pl-kos">}</span></span> Atlassian tools in <span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">access_level</span><span class="pl-kos">}</span></span> mode"</span>)</td>
        </tr>
        <tr>
          <td id="file-worker_mcp_atlassian-py-L41" class="blob-num js-line-number js-blob-rnum" data-line-number="41"></td>
          <td id="file-worker_mcp_atlassian-py-LC41" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">return</span> <span class="pl-s1">atlassian_mcp_client</span>, <span class="pl-s1">atlassian_tools</span></td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/4264b7402423ca76d40b1aa5e3725364/raw/dbf48f1e6cb45b3d192ffb15e910e1885a28ee2f/worker_mcp_atlassian.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/4264b7402423ca76d40b1aa5e3725364#file-worker_mcp_atlassian-py" class="Link--inTextBlock">
          worker_mcp_atlassian.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>What&#8217;s particularly clever about the Atlassian integration is how it handles both Jira tickets and Confluence documentation in a unified way. When someone asks about a project, the bot can pull information from both systems and present a complete picture.</p><p>The key insight with all these integrations is that they&#8217;re designed to fail gracefully. If a service is unavailable or credentials are missing, the bot continues to work - it just operates without those specific tools. This makes the system much more resilient in production environments.</p><p><strong>Terraform Container Deployment: Building and Deploying Docker Images</strong></p><p>One of the things I really appreciate about this architecture is how Terraform handles the entire container lifecycle. Unlike traditional Lambda deployments where you&#8217;re uploading zip files and hoping for the best, this approach treats the container image as the deployment artifact and manages everything from build to deployment in a single workflow.</p><p>The magic happens through a combination of ECR repository management and some clever null_resource provisioners that handle the Docker build and push operations. When you run `terraform apply`, it doesn&#8217;t just update your Lambda configuration - it actually builds a fresh container image, pushes it to ECR, and then updates the Lambda function to use that new image.</p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist141197238\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-terraform_container_deployment-tf\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-hcl  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;terraform_container_deployment.tf content, created by KyMidd on 07:44PM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;4\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;terraform_container_deployment.tf\&quot;>\n        <tr>\n          <td id=\&quot;file-terraform_container_deployment-tf-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-terraform_container_deployment-tf-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=\&quot;pl-c\&quot;><span class=\&quot;pl-c\&quot;>#</span> ECR Repository for container images<span class=\&quot;pl-c\&quot;></span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-terraform_container_deployment-tf-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-terraform_container_deployment-tf-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=\&quot;pl-en\&quot;>resource</span> <span class=\&quot;pl-smi\&quot;>&amp;quot;aws_ecr_repository&amp;quot;</span> <span class=\&quot;pl-smi\&quot;>&amp;quot;worker_container&amp;quot;</span> {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-terraform_container_deployment-tf-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-terraform_container_deployment-tf-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  <span class=\&quot;pl-v\&quot;><span class=\&quot;pl-smi\&quot;>name</span>                 <span class=\&quot;pl-k\&quot;>=</span> </span><span class=\&quot;pl-s\&quot;><span class=\&quot;pl-pds\&quot;>&amp;quot;</span><span class=\&quot;pl-k\&quot;>${</span><span class=\&quot;pl-smi\&quot;>var</span><span class=\&quot;pl-k\&quot;>.</span><span class=\&quot;pl-smi\&quot;>bot_name</span><span class=\&quot;pl-k\&quot;>}</span>-worker<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-terraform_container_deployment-tf-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-terraform_container_deployment-tf-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  <span class=\&quot;pl-v\&quot;><span class=\&quot;pl-smi\&quot;>image_tag_mutability</span> <span class=\&quot;pl-k\&quot;>=</span> </span><span class=\&quot;pl-s\&quot;><span class=\&quot;pl-pds\&quot;>&amp;quot;</span>MUTABLE<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-terraform_container_deployment-tf-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-terraform_container_deployment-tf-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  <span class=\&quot;pl-en\&quot;>image_scanning_configuration</span> {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-terraform_container_deployment-tf-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-terraform_container_deployment-tf-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=\&quot;pl-v\&quot;><span class=\&quot;pl-smi\&quot;>scan_on_push</span> <span class=\&quot;pl-k\&quot;>=</span> </span><span class=\&quot;pl-c1\&quot;>true</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-terraform_container_deployment-tf-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-terraform_container_deployment-tf-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  }</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-terraform_container_deployment-tf-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-terraform_container_deployment-tf-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  <span class=\&quot;pl-en\&quot;>lifecycle_policy</span> {}</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-terraform_container_deployment-tf-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-terraform_container_deployment-tf-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>}</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-terraform_container_deployment-tf-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-terraform_container_deployment-tf-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-terraform_container_deployment-tf-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-terraform_container_deployment-tf-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=\&quot;pl-c\&quot;><span class=\&quot;pl-c\&quot;>#</span> Build and push Docker image using null_resource<span class=\&quot;pl-c\&quot;></span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-terraform_container_deployment-tf-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-terraform_container_deployment-tf-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=\&quot;pl-en\&quot;>resource</span> <span class=\&quot;pl-smi\&quot;>&amp;quot;null_resource&amp;quot;</span> <span class=\&quot;pl-smi\&quot;>&amp;quot;docker_build_push&amp;quot;</span> {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-terraform_container_deployment-tf-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-terraform_container_deployment-tf-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  <span class=\&quot;pl-v\&quot;><span class=\&quot;pl-smi\&quot;>triggers</span> <span class=\&quot;pl-k\&quot;>=</span> </span>{</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-terraform_container_deployment-tf-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-terraform_container_deployment-tf-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=\&quot;pl-c\&quot;><span class=\&quot;pl-c\&quot;>#</span> Rebuild when any source file changes<span class=\&quot;pl-c\&quot;></span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-terraform_container_deployment-tf-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-terraform_container_deployment-tf-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    source_hash <span class=\&quot;pl-k\&quot;>=</span> data.archive_file.lambda_source.output_md5</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-terraform_container_deployment-tf-L16\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;16\&quot;></td>\n          <td id=\&quot;file-terraform_container_deployment-tf-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  }</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-terraform_container_deployment-tf-L17\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;17\&quot;></td>\n          <td id=\&quot;file-terraform_container_deployment-tf-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  <span class=\&quot;pl-en\&quot;>provisioner</span> <span class=\&quot;pl-smi\&quot;>&amp;quot;local-exec&amp;quot;</span> {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-terraform_container_deployment-tf-L18\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;18\&quot;></td>\n          <td id=\&quot;file-terraform_container_deployment-tf-LC18\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=\&quot;pl-v\&quot;><span class=\&quot;pl-smi\&quot;>command</span> <span class=\&quot;pl-k\&quot;>=</span> </span><span class=\&quot;pl-s\&quot;><span class=\&quot;pl-k\&quot;>&amp;lt;&amp;lt;-</span><span class=\&quot;pl-k\&quot;>EOF</span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-terraform_container_deployment-tf-L19\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;19\&quot;></td>\n          <td id=\&quot;file-terraform_container_deployment-tf-LC19\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=\&quot;pl-s\&quot;>      # Login to ECR</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-terraform_container_deployment-tf-L20\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;20\&quot;></td>\n          <td id=\&quot;file-terraform_container_deployment-tf-LC20\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=\&quot;pl-s\&quot;>      aws ecr get-login-password --region <span class=\&quot;pl-k\&quot;>${</span><span class=\&quot;pl-smi\&quot;>data</span><span class=\&quot;pl-k\&quot;>.</span><span class=\&quot;pl-smi\&quot;>aws_region</span><span class=\&quot;pl-k\&quot;>.</span><span class=\&quot;pl-smi\&quot;>current</span><span class=\&quot;pl-k\&quot;>.</span><span class=\&quot;pl-smi\&quot;>name</span><span class=\&quot;pl-k\&quot;>}</span> | docker login --username AWS --password-stdin <span class=\&quot;pl-k\&quot;>${</span><span class=\&quot;pl-smi\&quot;>aws_ecr_repository</span><span class=\&quot;pl-k\&quot;>.</span><span class=\&quot;pl-smi\&quot;>worker_container</span><span class=\&quot;pl-k\&quot;>.</span><span class=\&quot;pl-smi\&quot;>repository_url</span><span class=\&quot;pl-k\&quot;>}</span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-terraform_container_deployment-tf-L21\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;21\&quot;></td>\n          <td id=\&quot;file-terraform_container_deployment-tf-LC21\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=\&quot;pl-s\&quot;>      # Build image for ARM64</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-terraform_container_deployment-tf-L22\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;22\&quot;></td>\n          <td id=\&quot;file-terraform_container_deployment-tf-LC22\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=\&quot;pl-s\&quot;>      docker build --platform linux/arm64 -t <span class=\&quot;pl-k\&quot;>${</span><span class=\&quot;pl-smi\&quot;>aws_ecr_repository</span><span class=\&quot;pl-k\&quot;>.</span><span class=\&quot;pl-smi\&quot;>worker_container</span><span class=\&quot;pl-k\&quot;>.</span><span class=\&quot;pl-smi\&quot;>repository_url</span><span class=\&quot;pl-k\&quot;>}</span>:latest .</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-terraform_container_deployment-tf-L23\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;23\&quot;></td>\n          <td id=\&quot;file-terraform_container_deployment-tf-LC23\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=\&quot;pl-s\&quot;>      # Push image</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-terraform_container_deployment-tf-L24\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;24\&quot;></td>\n          <td id=\&quot;file-terraform_container_deployment-tf-LC24\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=\&quot;pl-s\&quot;>      docker push <span class=\&quot;pl-k\&quot;>${</span><span class=\&quot;pl-smi\&quot;>aws_ecr_repository</span><span class=\&quot;pl-k\&quot;>.</span><span class=\&quot;pl-smi\&quot;>worker_container</span><span class=\&quot;pl-k\&quot;>.</span><span class=\&quot;pl-smi\&quot;>repository_url</span><span class=\&quot;pl-k\&quot;>}</span>:latest</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-terraform_container_deployment-tf-L25\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;25\&quot;></td>\n          <td id=\&quot;file-terraform_container_deployment-tf-LC25\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=\&quot;pl-s\&quot;><span class=\&quot;pl-k\&quot;>    EOF</span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-terraform_container_deployment-tf-L26\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;26\&quot;></td>\n          <td id=\&quot;file-terraform_container_deployment-tf-LC26\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=\&quot;pl-v\&quot;><span class=\&quot;pl-smi\&quot;>working_dir</span> <span class=\&quot;pl-k\&quot;>=</span> </span><span class=\&quot;pl-s\&quot;><span class=\&quot;pl-pds\&quot;>&amp;quot;</span><span class=\&quot;pl-k\&quot;>${</span><span class=\&quot;pl-smi\&quot;>path</span><span class=\&quot;pl-k\&quot;>.</span><span class=\&quot;pl-smi\&quot;>module</span><span class=\&quot;pl-k\&quot;>}</span><span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-terraform_container_deployment-tf-L27\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;27\&quot;></td>\n          <td id=\&quot;file-terraform_container_deployment-tf-LC27\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  }</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-terraform_container_deployment-tf-L28\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;28\&quot;></td>\n          <td id=\&quot;file-terraform_container_deployment-tf-LC28\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  <span class=\&quot;pl-v\&quot;><span class=\&quot;pl-smi\&quot;>depends_on</span> <span class=\&quot;pl-k\&quot;>=</span> </span>[<span class=\&quot;pl-smi\&quot;>aws_ecr_repository</span><span class=\&quot;pl-k\&quot;>.</span><span class=\&quot;pl-smi\&quot;>worker_container</span>]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-terraform_container_deployment-tf-L29\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;29\&quot;></td>\n          <td id=\&quot;file-terraform_container_deployment-tf-LC29\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>}</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-terraform_container_deployment-tf-L30\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;30\&quot;></td>\n          <td id=\&quot;file-terraform_container_deployment-tf-LC30\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-terraform_container_deployment-tf-L31\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;31\&quot;></td>\n          <td id=\&quot;file-terraform_container_deployment-tf-LC31\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=\&quot;pl-c\&quot;><span class=\&quot;pl-c\&quot;>#</span> Worker Lambda function using container image<span class=\&quot;pl-c\&quot;></span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-terraform_container_deployment-tf-L32\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;32\&quot;></td>\n          <td id=\&quot;file-terraform_container_deployment-tf-LC32\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=\&quot;pl-en\&quot;>resource</span> <span class=\&quot;pl-smi\&quot;>&amp;quot;aws_lambda_function&amp;quot;</span> <span class=\&quot;pl-smi\&quot;>&amp;quot;worker&amp;quot;</span> {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-terraform_container_deployment-tf-L33\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;33\&quot;></td>\n          <td id=\&quot;file-terraform_container_deployment-tf-LC33\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  <span class=\&quot;pl-v\&quot;><span class=\&quot;pl-smi\&quot;>function_name</span> <span class=\&quot;pl-k\&quot;>=</span> </span><span class=\&quot;pl-s\&quot;><span class=\&quot;pl-pds\&quot;>&amp;quot;</span><span class=\&quot;pl-k\&quot;>${</span><span class=\&quot;pl-smi\&quot;>var</span><span class=\&quot;pl-k\&quot;>.</span><span class=\&quot;pl-smi\&quot;>bot_name</span><span class=\&quot;pl-k\&quot;>}</span>-Worker<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-terraform_container_deployment-tf-L34\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;34\&quot;></td>\n          <td id=\&quot;file-terraform_container_deployment-tf-LC34\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  <span class=\&quot;pl-v\&quot;><span class=\&quot;pl-smi\&quot;>role</span>          <span class=\&quot;pl-k\&quot;>=</span> </span>aws_iam_role<span class=\&quot;pl-k\&quot;>.</span><span class=\&quot;pl-smi\&quot;>lambda_execution_role</span><span class=\&quot;pl-k\&quot;>.</span><span class=\&quot;pl-smi\&quot;>arn</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-terraform_container_deployment-tf-L35\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;35\&quot;></td>\n          <td id=\&quot;file-terraform_container_deployment-tf-LC35\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-terraform_container_deployment-tf-L36\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;36\&quot;></td>\n          <td id=\&quot;file-terraform_container_deployment-tf-LC36\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  <span class=\&quot;pl-c\&quot;><span class=\&quot;pl-c\&quot;>#</span> Container configuration<span class=\&quot;pl-c\&quot;></span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-terraform_container_deployment-tf-L37\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;37\&quot;></td>\n          <td id=\&quot;file-terraform_container_deployment-tf-LC37\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  <span class=\&quot;pl-v\&quot;><span class=\&quot;pl-smi\&quot;>package_type</span> <span class=\&quot;pl-k\&quot;>=</span> </span><span class=\&quot;pl-s\&quot;><span class=\&quot;pl-pds\&quot;>&amp;quot;</span>Image<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-terraform_container_deployment-tf-L38\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;38\&quot;></td>\n          <td id=\&quot;file-terraform_container_deployment-tf-LC38\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  <span class=\&quot;pl-v\&quot;><span class=\&quot;pl-smi\&quot;>image_uri</span>    <span class=\&quot;pl-k\&quot;>=</span> </span><span class=\&quot;pl-s\&quot;><span class=\&quot;pl-pds\&quot;>&amp;quot;</span><span class=\&quot;pl-k\&quot;>${</span><span class=\&quot;pl-smi\&quot;>aws_ecr_repository</span><span class=\&quot;pl-k\&quot;>.</span><span class=\&quot;pl-smi\&quot;>worker_container</span><span class=\&quot;pl-k\&quot;>.</span><span class=\&quot;pl-smi\&quot;>repository_url</span><span class=\&quot;pl-k\&quot;>}</span>:latest<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-terraform_container_deployment-tf-L39\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;39\&quot;></td>\n          <td id=\&quot;file-terraform_container_deployment-tf-LC39\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  <span class=\&quot;pl-k\&quot;>...</span></td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/d2dfa09bfc0d1c3b89055d98a8e87183/raw/3b3c1f67970dad8b17d295c03d64c02e87b8a2ed/terraform_container_deployment.tf\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/d2dfa09bfc0d1c3b89055d98a8e87183#file-terraform_container_deployment-tf\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          terraform_container_deployment.tf\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-59543e005c9c.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-59543e005c9c.css"><div id="gist141197238" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-terraform_container_deployment-tf" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-hcl  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="4" data-paste-markdown-skip="" data-tagsearch-path="terraform_container_deployment.tf">
        <tbody><tr>
          <td id="file-terraform_container_deployment-tf-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-terraform_container_deployment-tf-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-c"><span class="pl-c">#</span> ECR Repository for container images<span class="pl-c"></span></span></td>
        </tr>
        <tr>
          <td id="file-terraform_container_deployment-tf-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-terraform_container_deployment-tf-LC2" class="blob-code blob-code-inner js-file-line"><span class="pl-en">resource</span> <span class="pl-smi">"aws_ecr_repository"</span> <span class="pl-smi">"worker_container"</span> {</td>
        </tr>
        <tr>
          <td id="file-terraform_container_deployment-tf-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-terraform_container_deployment-tf-LC3" class="blob-code blob-code-inner js-file-line">  <span class="pl-v"><span class="pl-smi">name</span>                 <span class="pl-k">=</span> </span><span class="pl-s"><span class="pl-pds">"</span><span class="pl-k">${</span><span class="pl-smi">var</span><span class="pl-k">.</span><span class="pl-smi">bot_name</span><span class="pl-k">}</span>-worker<span class="pl-pds">"</span></span></td>
        </tr>
        <tr>
          <td id="file-terraform_container_deployment-tf-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-terraform_container_deployment-tf-LC4" class="blob-code blob-code-inner js-file-line">  <span class="pl-v"><span class="pl-smi">image_tag_mutability</span> <span class="pl-k">=</span> </span><span class="pl-s"><span class="pl-pds">"</span>MUTABLE<span class="pl-pds">"</span></span></td>
        </tr>
        <tr>
          <td id="file-terraform_container_deployment-tf-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-terraform_container_deployment-tf-LC5" class="blob-code blob-code-inner js-file-line">  <span class="pl-en">image_scanning_configuration</span> {</td>
        </tr>
        <tr>
          <td id="file-terraform_container_deployment-tf-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-terraform_container_deployment-tf-LC6" class="blob-code blob-code-inner js-file-line">    <span class="pl-v"><span class="pl-smi">scan_on_push</span> <span class="pl-k">=</span> </span><span class="pl-c1">true</span></td>
        </tr>
        <tr>
          <td id="file-terraform_container_deployment-tf-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-terraform_container_deployment-tf-LC7" class="blob-code blob-code-inner js-file-line">  }</td>
        </tr>
        <tr>
          <td id="file-terraform_container_deployment-tf-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-terraform_container_deployment-tf-LC8" class="blob-code blob-code-inner js-file-line">  <span class="pl-en">lifecycle_policy</span> {}</td>
        </tr>
        <tr>
          <td id="file-terraform_container_deployment-tf-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-terraform_container_deployment-tf-LC9" class="blob-code blob-code-inner js-file-line">}</td>
        </tr>
        <tr>
          <td id="file-terraform_container_deployment-tf-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-terraform_container_deployment-tf-LC10" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-terraform_container_deployment-tf-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-terraform_container_deployment-tf-LC11" class="blob-code blob-code-inner js-file-line"><span class="pl-c"><span class="pl-c">#</span> Build and push Docker image using null_resource<span class="pl-c"></span></span></td>
        </tr>
        <tr>
          <td id="file-terraform_container_deployment-tf-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-terraform_container_deployment-tf-LC12" class="blob-code blob-code-inner js-file-line"><span class="pl-en">resource</span> <span class="pl-smi">"null_resource"</span> <span class="pl-smi">"docker_build_push"</span> {</td>
        </tr>
        <tr>
          <td id="file-terraform_container_deployment-tf-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-terraform_container_deployment-tf-LC13" class="blob-code blob-code-inner js-file-line">  <span class="pl-v"><span class="pl-smi">triggers</span> <span class="pl-k">=</span> </span>{</td>
        </tr>
        <tr>
          <td id="file-terraform_container_deployment-tf-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-terraform_container_deployment-tf-LC14" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"><span class="pl-c">#</span> Rebuild when any source file changes<span class="pl-c"></span></span></td>
        </tr>
        <tr>
          <td id="file-terraform_container_deployment-tf-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-terraform_container_deployment-tf-LC15" class="blob-code blob-code-inner js-file-line">    source_hash <span class="pl-k">=</span> data.archive_file.lambda_source.output_md5</td>
        </tr>
        <tr>
          <td id="file-terraform_container_deployment-tf-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-terraform_container_deployment-tf-LC16" class="blob-code blob-code-inner js-file-line">  }</td>
        </tr>
        <tr>
          <td id="file-terraform_container_deployment-tf-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-terraform_container_deployment-tf-LC17" class="blob-code blob-code-inner js-file-line">  <span class="pl-en">provisioner</span> <span class="pl-smi">"local-exec"</span> {</td>
        </tr>
        <tr>
          <td id="file-terraform_container_deployment-tf-L18" class="blob-num js-line-number js-blob-rnum" data-line-number="18"></td>
          <td id="file-terraform_container_deployment-tf-LC18" class="blob-code blob-code-inner js-file-line">    <span class="pl-v"><span class="pl-smi">command</span> <span class="pl-k">=</span> </span><span class="pl-s"><span class="pl-k">&lt;&lt;-</span><span class="pl-k">EOF</span></span></td>
        </tr>
        <tr>
          <td id="file-terraform_container_deployment-tf-L19" class="blob-num js-line-number js-blob-rnum" data-line-number="19"></td>
          <td id="file-terraform_container_deployment-tf-LC19" class="blob-code blob-code-inner js-file-line"><span class="pl-s">      # Login to ECR</span></td>
        </tr>
        <tr>
          <td id="file-terraform_container_deployment-tf-L20" class="blob-num js-line-number js-blob-rnum" data-line-number="20"></td>
          <td id="file-terraform_container_deployment-tf-LC20" class="blob-code blob-code-inner js-file-line"><span class="pl-s">      aws ecr get-login-password --region <span class="pl-k">${</span><span class="pl-smi">data</span><span class="pl-k">.</span><span class="pl-smi">aws_region</span><span class="pl-k">.</span><span class="pl-smi">current</span><span class="pl-k">.</span><span class="pl-smi">name</span><span class="pl-k">}</span> | docker login --username AWS --password-stdin <span class="pl-k">${</span><span class="pl-smi">aws_ecr_repository</span><span class="pl-k">.</span><span class="pl-smi">worker_container</span><span class="pl-k">.</span><span class="pl-smi">repository_url</span><span class="pl-k">}</span></span></td>
        </tr>
        <tr>
          <td id="file-terraform_container_deployment-tf-L21" class="blob-num js-line-number js-blob-rnum" data-line-number="21"></td>
          <td id="file-terraform_container_deployment-tf-LC21" class="blob-code blob-code-inner js-file-line"><span class="pl-s">      # Build image for ARM64</span></td>
        </tr>
        <tr>
          <td id="file-terraform_container_deployment-tf-L22" class="blob-num js-line-number js-blob-rnum" data-line-number="22"></td>
          <td id="file-terraform_container_deployment-tf-LC22" class="blob-code blob-code-inner js-file-line"><span class="pl-s">      docker build --platform linux/arm64 -t <span class="pl-k">${</span><span class="pl-smi">aws_ecr_repository</span><span class="pl-k">.</span><span class="pl-smi">worker_container</span><span class="pl-k">.</span><span class="pl-smi">repository_url</span><span class="pl-k">}</span>:latest .</span></td>
        </tr>
        <tr>
          <td id="file-terraform_container_deployment-tf-L23" class="blob-num js-line-number js-blob-rnum" data-line-number="23"></td>
          <td id="file-terraform_container_deployment-tf-LC23" class="blob-code blob-code-inner js-file-line"><span class="pl-s">      # Push image</span></td>
        </tr>
        <tr>
          <td id="file-terraform_container_deployment-tf-L24" class="blob-num js-line-number js-blob-rnum" data-line-number="24"></td>
          <td id="file-terraform_container_deployment-tf-LC24" class="blob-code blob-code-inner js-file-line"><span class="pl-s">      docker push <span class="pl-k">${</span><span class="pl-smi">aws_ecr_repository</span><span class="pl-k">.</span><span class="pl-smi">worker_container</span><span class="pl-k">.</span><span class="pl-smi">repository_url</span><span class="pl-k">}</span>:latest</span></td>
        </tr>
        <tr>
          <td id="file-terraform_container_deployment-tf-L25" class="blob-num js-line-number js-blob-rnum" data-line-number="25"></td>
          <td id="file-terraform_container_deployment-tf-LC25" class="blob-code blob-code-inner js-file-line"><span class="pl-s"><span class="pl-k">    EOF</span></span></td>
        </tr>
        <tr>
          <td id="file-terraform_container_deployment-tf-L26" class="blob-num js-line-number js-blob-rnum" data-line-number="26"></td>
          <td id="file-terraform_container_deployment-tf-LC26" class="blob-code blob-code-inner js-file-line">    <span class="pl-v"><span class="pl-smi">working_dir</span> <span class="pl-k">=</span> </span><span class="pl-s"><span class="pl-pds">"</span><span class="pl-k">${</span><span class="pl-smi">path</span><span class="pl-k">.</span><span class="pl-smi">module</span><span class="pl-k">}</span><span class="pl-pds">"</span></span></td>
        </tr>
        <tr>
          <td id="file-terraform_container_deployment-tf-L27" class="blob-num js-line-number js-blob-rnum" data-line-number="27"></td>
          <td id="file-terraform_container_deployment-tf-LC27" class="blob-code blob-code-inner js-file-line">  }</td>
        </tr>
        <tr>
          <td id="file-terraform_container_deployment-tf-L28" class="blob-num js-line-number js-blob-rnum" data-line-number="28"></td>
          <td id="file-terraform_container_deployment-tf-LC28" class="blob-code blob-code-inner js-file-line">  <span class="pl-v"><span class="pl-smi">depends_on</span> <span class="pl-k">=</span> </span>[<span class="pl-smi">aws_ecr_repository</span><span class="pl-k">.</span><span class="pl-smi">worker_container</span>]</td>
        </tr>
        <tr>
          <td id="file-terraform_container_deployment-tf-L29" class="blob-num js-line-number js-blob-rnum" data-line-number="29"></td>
          <td id="file-terraform_container_deployment-tf-LC29" class="blob-code blob-code-inner js-file-line">}</td>
        </tr>
        <tr>
          <td id="file-terraform_container_deployment-tf-L30" class="blob-num js-line-number js-blob-rnum" data-line-number="30"></td>
          <td id="file-terraform_container_deployment-tf-LC30" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-terraform_container_deployment-tf-L31" class="blob-num js-line-number js-blob-rnum" data-line-number="31"></td>
          <td id="file-terraform_container_deployment-tf-LC31" class="blob-code blob-code-inner js-file-line"><span class="pl-c"><span class="pl-c">#</span> Worker Lambda function using container image<span class="pl-c"></span></span></td>
        </tr>
        <tr>
          <td id="file-terraform_container_deployment-tf-L32" class="blob-num js-line-number js-blob-rnum" data-line-number="32"></td>
          <td id="file-terraform_container_deployment-tf-LC32" class="blob-code blob-code-inner js-file-line"><span class="pl-en">resource</span> <span class="pl-smi">"aws_lambda_function"</span> <span class="pl-smi">"worker"</span> {</td>
        </tr>
        <tr>
          <td id="file-terraform_container_deployment-tf-L33" class="blob-num js-line-number js-blob-rnum" data-line-number="33"></td>
          <td id="file-terraform_container_deployment-tf-LC33" class="blob-code blob-code-inner js-file-line">  <span class="pl-v"><span class="pl-smi">function_name</span> <span class="pl-k">=</span> </span><span class="pl-s"><span class="pl-pds">"</span><span class="pl-k">${</span><span class="pl-smi">var</span><span class="pl-k">.</span><span class="pl-smi">bot_name</span><span class="pl-k">}</span>-Worker<span class="pl-pds">"</span></span></td>
        </tr>
        <tr>
          <td id="file-terraform_container_deployment-tf-L34" class="blob-num js-line-number js-blob-rnum" data-line-number="34"></td>
          <td id="file-terraform_container_deployment-tf-LC34" class="blob-code blob-code-inner js-file-line">  <span class="pl-v"><span class="pl-smi">role</span>          <span class="pl-k">=</span> </span>aws_iam_role<span class="pl-k">.</span><span class="pl-smi">lambda_execution_role</span><span class="pl-k">.</span><span class="pl-smi">arn</span></td>
        </tr>
        <tr>
          <td id="file-terraform_container_deployment-tf-L35" class="blob-num js-line-number js-blob-rnum" data-line-number="35"></td>
          <td id="file-terraform_container_deployment-tf-LC35" class="blob-code blob-code-inner js-file-line">  </td>
        </tr>
        <tr>
          <td id="file-terraform_container_deployment-tf-L36" class="blob-num js-line-number js-blob-rnum" data-line-number="36"></td>
          <td id="file-terraform_container_deployment-tf-LC36" class="blob-code blob-code-inner js-file-line">  <span class="pl-c"><span class="pl-c">#</span> Container configuration<span class="pl-c"></span></span></td>
        </tr>
        <tr>
          <td id="file-terraform_container_deployment-tf-L37" class="blob-num js-line-number js-blob-rnum" data-line-number="37"></td>
          <td id="file-terraform_container_deployment-tf-LC37" class="blob-code blob-code-inner js-file-line">  <span class="pl-v"><span class="pl-smi">package_type</span> <span class="pl-k">=</span> </span><span class="pl-s"><span class="pl-pds">"</span>Image<span class="pl-pds">"</span></span></td>
        </tr>
        <tr>
          <td id="file-terraform_container_deployment-tf-L38" class="blob-num js-line-number js-blob-rnum" data-line-number="38"></td>
          <td id="file-terraform_container_deployment-tf-LC38" class="blob-code blob-code-inner js-file-line">  <span class="pl-v"><span class="pl-smi">image_uri</span>    <span class="pl-k">=</span> </span><span class="pl-s"><span class="pl-pds">"</span><span class="pl-k">${</span><span class="pl-smi">aws_ecr_repository</span><span class="pl-k">.</span><span class="pl-smi">worker_container</span><span class="pl-k">.</span><span class="pl-smi">repository_url</span><span class="pl-k">}</span>:latest<span class="pl-pds">"</span></span></td>
        </tr>
        <tr>
          <td id="file-terraform_container_deployment-tf-L39" class="blob-num js-line-number js-blob-rnum" data-line-number="39"></td>
          <td id="file-terraform_container_deployment-tf-LC39" class="blob-code blob-code-inner js-file-line">  <span class="pl-k">...</span></td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/d2dfa09bfc0d1c3b89055d98a8e87183/raw/3b3c1f67970dad8b17d295c03d64c02e87b8a2ed/terraform_container_deployment.tf" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/d2dfa09bfc0d1c3b89055d98a8e87183#file-terraform_container_deployment-tf" class="Link--inTextBlock">
          terraform_container_deployment.tf
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>The ECR repository includes lifecycle policies that automatically clean up old images, which is important because container images can get pretty large. We keep the latest 10 tagged images and automatically delete untagged images after a day. This prevents your ECR costs from spiraling out of control as you iterate on the deployment.</p><p>The Docker build process is particularly clever here. The null_resource uses a trigger based on the source file hash, so it only rebuilds the image when your code actually changes. This saves a ton of time during development when you&#8217;re just tweaking Terraform configurations without touching the application code.</p><p>What&#8217;s really nice about this approach is how it handles the ARM64 build process. The `--platform linux/arm64` flag ensures we&#8217;re building for the correct architecture, and since we&#8217;re building on the same platform where Terraform runs, we avoid all those cross-compilation headaches that made Lambda layers so problematic.</p><p>The Lambda function configuration shows how seamlessly containers integrate with the rest of AWS Lambda. You still get all the same configuration options - memory size, timeout, environment variables - but now your entire dependency tree is packaged as a single, immutable artifact. No more worrying about layer compatibility or missing dependencies.</p><p>Environment variables are passed through cleanly, including the MCP feature flags that control which integrations are enabled. This means you can deploy the same container image to different environments with different capabilities just by changing the Terraform variables.</p><h1><strong>Lessons Learned and Next Steps</strong></h1><p>Building this enhanced version of Vera taught me quite a few things about modern agentic AI architectures, and honestly, some hard lessons about when to stick with what works versus when to embrace new approaches.</p><p><strong>Container vs Layers: The Right Tool for the Job</strong></p><p>The biggest lesson here was accepting that Lambda layers, while great for simple shared dependencies, just weren&#8217;t going to cut it for this complexity level. The original Vera worked fine with layers because it had a handful of lightweight dependencies. But once you&#8217;re dealing with the Strands framework, multiple MCP clients, and all their transitive dependencies, containers become the only sane choice.</p><p>The decision framework I&#8217;d use now: if your requirements.txt has more than 20 packages or includes any packages that need native compilation, just go straight to containers. The development experience is so much better, and the deployment reliability gains are worth the slightly larger cold start times.</p><p><strong>Performance Considerations</strong></p><p>Speaking of cold starts, they&#8217;re definitely longer with the container approach - usually 2-3 seconds instead of sub-second for the original Vera. But here&#8217;s the thing: for a Slack bot, this rarely matters. Most conversations happen in bursts, so the function stays warm. And when it does cold start, users are already expecting some delay for &#8220;AI thinking time.&#8221;</p><p>What surprised me was how much faster the warm execution became. All those dependencies are pre-resolved and ready to go, which makes the actual AI processing noticeably quicker. The MCP clients can establish connections faster, and there&#8217;s less Python import overhead.</p><p><strong>Scaling Challenges and Solutions</strong></p><p>The current architecture handles our team&#8217;s usage just fine, but I can see some potential bottlenecks as usage grows. Each Lambda invocation opens its own MCP client connections, which could become problematic if we&#8217;re hitting rate limits on external APIs.</p><p>The next evolution would probably involve connection pooling or even moving some of the MCP operations to separate services that can maintain persistent connections. But honestly, that&#8217;s a nice problem to have - it means people are actually using the bot.</p><p><strong>Modular Architecture Payoffs</strong></p><p>The decision to break everything into focused modules has paid dividends already. When GitHub changed their API authentication requirements, I only had to touch `worker_mcp_github.py`. When we wanted to adjust the system prompt for better Slack formatting, it was all in `worker_inputs.py`.</p><p>This modularity also makes testing much more manageable. You can unit test individual MCP integrations without spinning up the entire Lambda environment, which speeds up the development cycle considerably.</p><p><strong>Future Enhancement Opportunities</strong></p><p>There are several directions this could evolve. Adding more MCP integrations is the obvious one - imagine connecting to ServiceNow, Datadog, or your internal APIs. The framework makes this relatively straightforward now.</p><p>I&#8217;m also interested in exploring memory and context persistence. Right now, each conversation is stateless, but there&#8217;s real value in the bot remembering previous interactions or maintaining context across related conversations.</p><p>The Strands framework also supports more advanced agentic behaviors like multi-step planning and tool chaining. We&#8217;re barely scratching the surface of what&#8217;s possible when you give an AI system the ability to orchestrate multiple API calls to solve complex problems.</p><h1><strong>Summary</strong></h1><p>It&#8217;s been super fun to build out this new platform, and I&#8217;ve been able to re-use many of the pieces of the original genAI Vera, particularly the pieces that integrate with Slack. </p><p>The bot is quite a bit slower than genAI Vera, but can answer questions like this: </p><ul><li><p>&#8220;Show me critical incidents for my team&#8221; - pulls live PagerDuty data</p></li><li><p>&#8220;Status of the auth PR?&#8221; - queries GitHub directly</p></li><li><p>&#8220;Find our API rate limiting docs&#8221; - searches Confluence with links</p></li><li><p>&#8220;What&#8217;s blocked this sprint?&#8221; - filtered Jira queries</p></li></ul><p>And because of using MCP tools, the answers are less often hallucinated, and more often backed by information the tool fetches. </p><p>Instead of bouncing between Slack, PagerDuty, GitHub, and Jira for incident context, teams get comprehensive answers from one question. On-call engineers can query previous incidents, service status, and recent code changes without leaving their response channel.</p><p>Hopefully it can help you too. Good luck out there! <br>kyle</p>]]></content:encoded></item><item><title><![CDATA[🔥AWS Strands can't read GitHub Files with MCP🔥]]></title><description><![CDATA[aka, Are you sure you don't want to accept my PR to fix this?]]></description><link>https://www.letsdodevops.com/p/aws-strands-cant-read-github-files</link><guid isPermaLink="false">https://www.letsdodevops.com/p/aws-strands-cant-read-github-files</guid><dc:creator><![CDATA[Kyler Middleton]]></dc:creator><pubDate>Wed, 10 Sep 2025 14:30:52 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!88Tu!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F870e364b-b9d6-4e17-832c-7cb5f3d9edae_2430x898.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote><p><em>This blog series focuses on presenting complex DevOps projects as simple and approachable via plain language and lots of pictures. You can do it!</em></p><p>These articles are supported by readers, <a href="https://www.letsdodevops.com/subscribe">please consider subscribing</a> to support me writing more of these articles &lt;3 :)</p></blockquote><p>Hey all!</p><p>I&#8217;ve been <a href="https://www.letsdodevops.com/p/aws-strands-and-mcp-with-examples">playing around with Strands</a> a great deal, and having a ton of fun. <a href="https://strandsagents.com/latest/">Strands Agents</a> is a python library that permits writing agentic workloads with relatively little code, and supports all sorts of AI backends to provide the AI services, including Amazon Bedrock, OpenAI, Anthropic, and even local models on your computer. </p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.letsdodevops.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Let's Do DevOps is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>Here&#8217;s one that fetches vectors from an AWS Bedrock Knowledge Base, feeds those vectors into an Agent (by default Claude 4), and streams an answer to the provided question - all in 4 lines! That&#8217;s absolutely absurd, it&#8217;s so cool. </p><pre><code>from strands import Agent
from strands_tools import retrieve
agent = Agent(tools=[retrieve])
agent("Tell me about the cloud platform team")</code></pre><p>One of the powers of Strands is it easily integrates with MCPs - those tool integrations that permit your agent to interact with the platforms your business uses to do stuff. Strands can use MCPs to look at:</p><ul><li><p>Your tickets in Jira? Yep! </p></li><li><p>Your outage information from PagerDuty? Yep! </p></li><li><p>Your code on GitHub? <strong>Well, sorta</strong></p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!88Tu!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F870e364b-b9d6-4e17-832c-7cb5f3d9edae_2430x898.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!88Tu!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F870e364b-b9d6-4e17-832c-7cb5f3d9edae_2430x898.png 424w, https://substackcdn.com/image/fetch/$s_!88Tu!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F870e364b-b9d6-4e17-832c-7cb5f3d9edae_2430x898.png 848w, https://substackcdn.com/image/fetch/$s_!88Tu!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F870e364b-b9d6-4e17-832c-7cb5f3d9edae_2430x898.png 1272w, https://substackcdn.com/image/fetch/$s_!88Tu!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F870e364b-b9d6-4e17-832c-7cb5f3d9edae_2430x898.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!88Tu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F870e364b-b9d6-4e17-832c-7cb5f3d9edae_2430x898.png" width="1456" height="538" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/870e364b-b9d6-4e17-832c-7cb5f3d9edae_2430x898.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:538,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:272459,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.letsdodevops.com/i/173229165?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F870e364b-b9d6-4e17-832c-7cb5f3d9edae_2430x898.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!88Tu!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F870e364b-b9d6-4e17-832c-7cb5f3d9edae_2430x898.png 424w, https://substackcdn.com/image/fetch/$s_!88Tu!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F870e364b-b9d6-4e17-832c-7cb5f3d9edae_2430x898.png 848w, https://substackcdn.com/image/fetch/$s_!88Tu!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F870e364b-b9d6-4e17-832c-7cb5f3d9edae_2430x898.png 1272w, https://substackcdn.com/image/fetch/$s_!88Tu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F870e364b-b9d6-4e17-832c-7cb5f3d9edae_2430x898.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Because &#8220;Resources&#8221; aren&#8217;t built out yet, Strands agents can&#8217;t yet read the contents of files from GitHub. </p><p>Lets talk about what&#8217;s missing and how to fix it! </p><h1>Wait, What are we talking about? </h1><p>There&#8217;s a lot going on here, so it&#8217;s important to understand the players. </p><p>First, there are <strong>AI clients</strong> - these provide the AI services from a platform, for example AWS Bedrock or OpenAI. You generally get a token from these platforms, export the token into your terminal, and you can then use tools that trigger jobs on these platforms. </p><blockquote><p>If you want to be fancy, you can say &#8220;requests for inference&#8221; instead of &#8220;jobs&#8221;. They have the same meaning. </p></blockquote><p>Then there are <strong>MCP clients</strong>. These are the software components that can talk to MCP servers, potentially authenticate, and download the tools lists from the MCP server. Often this is your IDE like VSCode or Claude Code. Strands has an MCP client built in. </p><p>Then there are <strong>MCP servers</strong>. These can exist anywhere, but we&#8217;re seeing more and more of these as remote, first party MCP servers. That means the company who hosts the MCP server (remote to your client, like over the internet) is the place you&#8217;d use those tools. Think accessing a GitHub MCP from a GitHub URL in order to download tools to interact with GitHub&#8217;s codebase. </p><p>The platforms that store enterprise data have a great motivator to support MCP - all the enterprises that are charging into AI adoption can more easily consume those platforms if they can access an MCP for the platform. Early stage companies that are much more AI focused could easily use MCP availability as a primary differentiator when choosing which platforms to use. </p><h1>The problem</h1><p>So there&#8217;s a GitHub MCP! And it works great. When I first started working with it, I assumed it wasn&#8217;t completely implemented - it just entered <a href="https://github.blog/changelog/2025-09-04-remote-github-mcp-server-is-now-generally-available/">General Availability (GA) Sept 4, 2025</a>, and I was playing with it in Strands a few weeks before that. </p><p>It had this odd ability to read ALL the data from GitHub, but it couldn&#8217;t download file contents. Weird, but whatever. </p><p>However, when I tested the same GitHub MCP with VS Code IDE, it worked great. </p><p>So I tested it with Claude Code, worked great. </p><blockquote><p>It turns out ONLY Strands has a problem downloading file content from GitHub when using MCP. </p></blockquote><p>I dug into the problem with some logging and found this log: </p><pre><code>2025-08-22 07:50:55,725 | INFO | Thread-1 (_background_task) | httpx | HTTP Request: POST https://api.githubcopilot.com/mcp/ "HTTP/1.1 200 OK"
# ...
2025-08-22 07:50:55,865 | DEBUG | MainThread | strands.tools.mcp.mcp_client | [Thread: MainThread, Session: 838664df-dafb-4a06-865e-91d618eb77f6] <strong>unhandled content type: EmbeddedResource - dropping content</strong>
2025-08-22 07:50:55,865 | DEBUG | MainThread | strands.tools.mcp.mcp_client | [Thread: MainThread, Session: 838664df-dafb-4a06-865e-91d618eb77f6] <strong>tool execution completed with status: success</strong></code></pre><p>There&#8217;s two interesting things going on here. When the Strands agent attempts to fetch file contents, it uses a tool called <a href="https://github.com/github/github-mcp-server/blob/9db2e17204d9588795b01db9407a7465b3db2411/pkg/github/repositories.go#L485">get_file_contents</a>. This tool is just an MCP instruction to use &#8220;<a href="https://docs.github.com/en/rest/repos/contents?apiVersion=2022-11-28#get-repository-content">Get Repository Contents</a>&#8221; API to fetch a file contents. </p><p>It reads the header to identify the type of file. If it starts with `application`, it decides that file is text/ascii-based. For example, a .tfvars file (terraform variables) is text based, and the header value gets: </p><pre><code>&lt; content-type: application/json; charset=utf-8</code></pre><p>After deciding the content is text, the GitHub MCP (really the Get Repository Contents REST API call), returns a payload like this: </p><pre><code>{
  "type": "resource",
  "resource": {
    "uri": "repo://orgName/repoName/contents/path/to/file.tfvars",
    "mimeType": "application/json; charset=utf-8",
    "text": "... (plain-text contents of the file) ..."
  }
}</code></pre><p>The Strands library expects to see direct TextResourceContents (on .text property path) or BlobResourceContents (on .blob property path) like this: </p><pre><code>{
  "text": "... (plain-text contents of the file) ..."
}</code></pre><p>Since the GitHub MCP is adding extra information, the Strands library totally fails to interpret it, despite the path being super duper close, and the information being nearly the same. </p><h1>How To Fix It</h1><p>You can&#8217;t. Well, at least not easily. The problem is in how the Strands library is written - it doesn&#8217;t yet have support for EmbeddedResources, which is the whole &#8220;.resource&#8221; property path and syntax. </p><p>I&#8217;ve created a fixed version on this PR: <a href="https://github.com/strands-agents/sdk-python/pull/726">https://github.com/strands-agents/sdk-python/pull/726</a></p><p>This fixes the behavior for many types of files that are text encoded from github, but doesn&#8217;t yet work for binary/blob type files. It does however work for these types of files: </p><pre><code>"application/json",
"application/xml",
"application/javascript",
"application/x-yaml",
"application/yaml",
"application/xhtml+xml",</code></pre><p>And you&#8217;d be surprised how many file types are covered with these! </p><p>Try it out: Copy my version (from the PR) of this file: src/strands/tools/mcp/mcp_client.py over your version, and watch how your Strands agent can now fetch file contents: </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!tYwU!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a56db73-4da3-4727-9615-82c9269c5d1f_1702x496.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!tYwU!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a56db73-4da3-4727-9615-82c9269c5d1f_1702x496.png 424w, https://substackcdn.com/image/fetch/$s_!tYwU!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a56db73-4da3-4727-9615-82c9269c5d1f_1702x496.png 848w, https://substackcdn.com/image/fetch/$s_!tYwU!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a56db73-4da3-4727-9615-82c9269c5d1f_1702x496.png 1272w, https://substackcdn.com/image/fetch/$s_!tYwU!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a56db73-4da3-4727-9615-82c9269c5d1f_1702x496.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!tYwU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a56db73-4da3-4727-9615-82c9269c5d1f_1702x496.png" width="1456" height="424" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6a56db73-4da3-4727-9615-82c9269c5d1f_1702x496.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:424,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:113801,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.letsdodevops.com/i/173229165?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a56db73-4da3-4727-9615-82c9269c5d1f_1702x496.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!tYwU!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a56db73-4da3-4727-9615-82c9269c5d1f_1702x496.png 424w, https://substackcdn.com/image/fetch/$s_!tYwU!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a56db73-4da3-4727-9615-82c9269c5d1f_1702x496.png 848w, https://substackcdn.com/image/fetch/$s_!tYwU!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a56db73-4da3-4727-9615-82c9269c5d1f_1702x496.png 1272w, https://substackcdn.com/image/fetch/$s_!tYwU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a56db73-4da3-4727-9615-82c9269c5d1f_1702x496.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h1>What&#8217;s Next? </h1><p>The Strands maintainers are hoping to implement full support for Resources before implementing partial support, meaning that this functionality isn&#8217;t coming immediately. Implementing the full lifecycle support for Resources will take a bit longer. </p><p>I&#8217;ll continue asking to merge this in - I think being able to fetch file contents from GitHub is a huge functionality gap for Strands, and will be relatively blocking for us. </p><p>Potentially we&#8217;ll need to implement some static tools for our bots to use to fetch file contents from GitHub using REST. I</p><p>And I&#8217;ll be sure to write about it. </p><p>Thanks all! Good luck out there. <br>kyler</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.letsdodevops.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Let's Do DevOps is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[🔥AWS Strands and MCP with Simple, Real-World Examples🔥]]></title><description><![CDATA[aka, robots with tools]]></description><link>https://www.letsdodevops.com/p/aws-strands-and-mcp-with-examples</link><guid isPermaLink="false">https://www.letsdodevops.com/p/aws-strands-and-mcp-with-examples</guid><dc:creator><![CDATA[Kyler Middleton]]></dc:creator><pubDate>Tue, 26 Aug 2025 14:02:04 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!PyDc!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fced7748b-ad25-475e-b61c-78052e29b37d_2272x822.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote><p><em>This blog series focuses on presenting complex DevOps projects as simple and approachable via plain language and lots of pictures. You can do it!</em></p><p>These articles are supported by readers, <a href="https://www.letsdodevops.com/subscribe">please consider subscribing</a> to support me writing more of these articles &lt;3 :)</p></blockquote><p>Hey all! </p><p>In previous articles we built generative AI bots that integrate with a RAG knowledge base using the AWS Bedrock backend. </p><ul><li><p><a href="https://www.letsdodevops.com/p/lets-do-devops-building-an-azure">AWS GenAI Bedrock Series</a></p></li><li><p><a href="https://www.letsdodevops.com/p/ai-teams-bot">Azure GenAI Bedrock Series</a></p></li></ul><p>They&#8217;re awesome, but they&#8217;re not agentic. </p><blockquote><p>"Agentic" describes <strong>the ability to act autonomously, make decisions, and achieve goals without constant human intervention</strong>, a concept central to agentic AI systems that can plan, execute tasks, and adapt to new information</p></blockquote><p>Basically, agentic bots do 1 step, and then return that information. That generally works really well for chatbots - read this information, return a summary. One step is all you need! </p><p>However, if you want a bot to do a multi-step process, that&#8217;s when you&#8217;re looking at an &#8220;agentic workflow&#8221;. Something like &#8220;Read our PagerDuty, isolate any resources with issues, then go look at AWS and see if the resources have an error state you can recommend fixes for&#8221; is multiple step - talk to platformA, analyze the data, then use that data to talk to platformB and analyze the data from there. </p><p>Agentic agents can do it! </p><p><a href="https://strandsagents.com/latest/">Strands</a> has emerged as a language that permits building effective and comprehensive bots with little python. It comes out of AWS, but the team there has made sure to open-source it (Apache 2.0 license) and make sure it&#8217;s compatible with lots of back-ends like OpenAI/ChatGPT, Anthropic, OLlama, and others. </p><p>It support agentic workflows, MCP (tool use to talk to platforms), A2A protocol (to talk to other bots and coordinate work), and will likely be kept up to date with AWS services in AgentCore like Memory and other cool stuff shaking out of that project. </p><p>It feels like a great place to start.</p><blockquote><p>Strands, find all the ec2 instances in us-east-1, and give me their IPs and AZs in a table.</p></blockquote><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!PyDc!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fced7748b-ad25-475e-b61c-78052e29b37d_2272x822.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!PyDc!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fced7748b-ad25-475e-b61c-78052e29b37d_2272x822.png 424w, https://substackcdn.com/image/fetch/$s_!PyDc!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fced7748b-ad25-475e-b61c-78052e29b37d_2272x822.png 848w, https://substackcdn.com/image/fetch/$s_!PyDc!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fced7748b-ad25-475e-b61c-78052e29b37d_2272x822.png 1272w, https://substackcdn.com/image/fetch/$s_!PyDc!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fced7748b-ad25-475e-b61c-78052e29b37d_2272x822.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!PyDc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fced7748b-ad25-475e-b61c-78052e29b37d_2272x822.png" width="1456" height="527" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ced7748b-ad25-475e-b61c-78052e29b37d_2272x822.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:527,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:184045,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.letsdodevops.com/i/171701804?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fced7748b-ad25-475e-b61c-78052e29b37d_2272x822.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!PyDc!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fced7748b-ad25-475e-b61c-78052e29b37d_2272x822.png 424w, https://substackcdn.com/image/fetch/$s_!PyDc!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fced7748b-ad25-475e-b61c-78052e29b37d_2272x822.png 848w, https://substackcdn.com/image/fetch/$s_!PyDc!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fced7748b-ad25-475e-b61c-78052e29b37d_2272x822.png 1272w, https://substackcdn.com/image/fetch/$s_!PyDc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fced7748b-ad25-475e-b61c-78052e29b37d_2272x822.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Sure, let me go grab that information for you</figcaption></figure></div><p>I&#8217;m going to be building some enterprise Strands agentic assistants over the next few months, but for now I&#8217;m just proving out really simple workflows, and I wanted to share them. They&#8217;re all here: </p><blockquote><p><a href="https://github.com/KyMidd/StrandsAgenticExamples">github.com/KyMidd/StrandsAgenticExamples</a></p></blockquote><p>We&#8217;ll walk through one by one for how they work. </p><h1>Simple Calculator</h1><p>AI Models are terrible at math. They just are. If you ask a model a math problem that it&#8217;s seen before in its data set, it&#8217;ll give you the answer it saw. Is it the right answer? It doesn&#8217;t know. </p><p>I&#8217;m also bad at math, so I use a calculator. Best idea to make an AI good at math? Give it a calculator! And that&#8217;s one of the built-in tools available to strands. </p><p>On line 1, we import the Agent library from strands, and on line 2 we import from the strands_tools library, specifically the built-in tool called &#8220;calculator&#8221;. </p><p>Then on line 3 we instantiate an agent and provide it the tool. </p><p>Then on line 4 we ask the agent to calculate the square root of 125. The agent sees a problem that it can resolve with math, so it uses the calculator. </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist140389073\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-simple_calc-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;simple_calc.py content, created by KyMidd on 12:35PM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;4\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;simple_calc.py\&quot;>\n        <tr>\n          <td id=\&quot;file-simple_calc-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-simple_calc-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>from</span> <span class=pl-s1>strands</span> <span class=pl-k>import</span> <span class=pl-v>Agent</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_calc-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-simple_calc-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>from</span> <span class=pl-s1>strands_tools</span> <span class=pl-k>import</span> <span class=pl-s1>calculator</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_calc-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-simple_calc-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s1>agent</span> <span class=pl-c1>=</span> <span class=pl-en>Agent</span>(<span class=pl-s1>tools</span><span class=pl-c1>=</span>[<span class=pl-s1>calculator</span>])</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_calc-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-simple_calc-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-en>agent</span>(<span class=pl-s>&amp;quot;What is the square root of 125&amp;quot;</span>)</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/bf15fd86b4aaf056e4cd4e684c148f0f/raw/75ea7d3fac46d5d12d180258f0b87691388c8dd8/simple_calc.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/bf15fd86b4aaf056e4cd4e684c148f0f#file-simple_calc-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          simple_calc.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-59543e005c9c.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-59543e005c9c.css"><div id="gist140389073" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-simple_calc-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="4" data-paste-markdown-skip="" data-tagsearch-path="simple_calc.py">
        <tbody><tr>
          <td id="file-simple_calc-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-simple_calc-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">from</span> <span class="pl-s1">strands</span> <span class="pl-k">import</span> <span class="pl-v">Agent</span></td>
        </tr>
        <tr>
          <td id="file-simple_calc-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-simple_calc-py-LC2" class="blob-code blob-code-inner js-file-line"><span class="pl-k">from</span> <span class="pl-s1">strands_tools</span> <span class="pl-k">import</span> <span class="pl-s1">calculator</span></td>
        </tr>
        <tr>
          <td id="file-simple_calc-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-simple_calc-py-LC3" class="blob-code blob-code-inner js-file-line"><span class="pl-s1">agent</span> <span class="pl-c1">=</span> <span class="pl-en">Agent</span>(<span class="pl-s1">tools</span><span class="pl-c1">=</span>[<span class="pl-s1">calculator</span>])</td>
        </tr>
        <tr>
          <td id="file-simple_calc-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-simple_calc-py-LC4" class="blob-code blob-code-inner js-file-line"><span class="pl-en">agent</span>(<span class="pl-s">"What is the square root of 125"</span>)</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/bf15fd86b4aaf056e4cd4e684c148f0f/raw/75ea7d3fac46d5d12d180258f0b87691388c8dd8/simple_calc.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/bf15fd86b4aaf056e4cd4e684c148f0f#file-simple_calc-py" class="Link--inTextBlock">
          simple_calc.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p> Here&#8217;s the output: </p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Kiwq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F893d0e17-d30c-455a-8cec-ac405d28e9a4_2108x252.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Kiwq!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F893d0e17-d30c-455a-8cec-ac405d28e9a4_2108x252.png 424w, https://substackcdn.com/image/fetch/$s_!Kiwq!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F893d0e17-d30c-455a-8cec-ac405d28e9a4_2108x252.png 848w, https://substackcdn.com/image/fetch/$s_!Kiwq!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F893d0e17-d30c-455a-8cec-ac405d28e9a4_2108x252.png 1272w, https://substackcdn.com/image/fetch/$s_!Kiwq!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F893d0e17-d30c-455a-8cec-ac405d28e9a4_2108x252.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Kiwq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F893d0e17-d30c-455a-8cec-ac405d28e9a4_2108x252.png" width="1456" height="174" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/893d0e17-d30c-455a-8cec-ac405d28e9a4_2108x252.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:174,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:73860,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.letsdodevops.com/i/171701804?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F893d0e17-d30c-455a-8cec-ac405d28e9a4_2108x252.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Kiwq!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F893d0e17-d30c-455a-8cec-ac405d28e9a4_2108x252.png 424w, https://substackcdn.com/image/fetch/$s_!Kiwq!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F893d0e17-d30c-455a-8cec-ac405d28e9a4_2108x252.png 848w, https://substackcdn.com/image/fetch/$s_!Kiwq!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F893d0e17-d30c-455a-8cec-ac405d28e9a4_2108x252.png 1272w, https://substackcdn.com/image/fetch/$s_!Kiwq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F893d0e17-d30c-455a-8cec-ac405d28e9a4_2108x252.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h1>Simple Remote MCP - AWS Docs</h1><p>Not all MCP servers require authentication - some provide tools you can use to do interesting stuff, like this remote MCP endpoint from AWS that lets you ask AWS itself about their documentation, and provide your bot the ability to read AWS documentation in its operation. </p><p>On lines 2-3, we import the MCPClient from the strands tools package, and on line 3 we import the standard i/o client and parameters. </p><p>On line 5, we define the aws_docs_client as a python lambda (small function), that&#8217;s calling the uvx tool to connect to the awslambda aws-documentation-mcp-server MCP. Notably, this doesn&#8217;t &#8220;run&#8221; the MCPClient yet, this just statically defines what will happen when it does. </p><p>On line 9, we define a &#8220;with&#8221;. This step actually runs the AWS Client config. </p><p>Then on line 10, we start the agent with the tools available from that MCP - note how we&#8217;re reading the tools list available from the remote MCP with list_tools_sync(). </p><p>Then on line 11, we&#8217;re asking the agent a question. </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist140389305\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-simple_remote_mcp-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;simple_remote_mcp.py content, created by KyMidd on 12:53PM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;4\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;simple_remote_mcp.py\&quot;>\n        <tr>\n          <td id=\&quot;file-simple_remote_mcp-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-simple_remote_mcp-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>from</span> <span class=pl-s1>strands</span> <span class=pl-k>import</span> <span class=pl-v>Agent</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_remote_mcp-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-simple_remote_mcp-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>from</span> <span class=pl-s1>strands</span>.<span class=pl-s1>tools</span>.<span class=pl-s1>mcp</span> <span class=pl-k>import</span> <span class=pl-v>MCPClient</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_remote_mcp-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-simple_remote_mcp-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>from</span> <span class=pl-s1>mcp</span> <span class=pl-k>import</span> <span class=pl-s1>stdio_client</span>, <span class=pl-v>StdioServerParameters</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_remote_mcp-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-simple_remote_mcp-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_remote_mcp-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-simple_remote_mcp-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s1>aws_docs_client</span> <span class=pl-c1>=</span> <span class=pl-en>MCPClient</span>(</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_remote_mcp-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-simple_remote_mcp-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>lambda</span>: <span class=pl-en>stdio_client</span>(<span class=pl-en>StdioServerParameters</span>(<span class=pl-s1>command</span><span class=pl-c1>=</span><span class=pl-s>&amp;quot;uvx&amp;quot;</span>, <span class=pl-s1>args</span><span class=pl-c1>=</span>[<span class=pl-s>&amp;quot;awslabs.aws-documentation-mcp-server@latest&amp;quot;</span>]))</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_remote_mcp-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-simple_remote_mcp-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_remote_mcp-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-simple_remote_mcp-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_remote_mcp-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-simple_remote_mcp-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>with</span> <span class=pl-s1>aws_docs_client</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_remote_mcp-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-simple_remote_mcp-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>   <span class=pl-s1>agent</span> <span class=pl-c1>=</span> <span class=pl-en>Agent</span>(<span class=pl-s1>tools</span><span class=pl-c1>=</span><span class=pl-s1>aws_docs_client</span>.<span class=pl-c1>list_tools_sync</span>())</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_remote_mcp-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-simple_remote_mcp-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>   <span class=pl-s1>response</span> <span class=pl-c1>=</span> <span class=pl-en>agent</span>(<span class=pl-s>&amp;quot;Tell me about Amazon Bedrock and how to use it with Python&amp;quot;</span>)</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/3902874d7524c30e5be9d173a70ff13c/raw/28e7ea995a3a7a71ab2e3f7208fa5e841ee64501/simple_remote_mcp.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/3902874d7524c30e5be9d173a70ff13c#file-simple_remote_mcp-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          simple_remote_mcp.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-59543e005c9c.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-59543e005c9c.css"><div id="gist140389305" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-simple_remote_mcp-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="4" data-paste-markdown-skip="" data-tagsearch-path="simple_remote_mcp.py">
        <tbody><tr>
          <td id="file-simple_remote_mcp-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-simple_remote_mcp-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">from</span> <span class="pl-s1">strands</span> <span class="pl-k">import</span> <span class="pl-v">Agent</span></td>
        </tr>
        <tr>
          <td id="file-simple_remote_mcp-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-simple_remote_mcp-py-LC2" class="blob-code blob-code-inner js-file-line"><span class="pl-k">from</span> <span class="pl-s1">strands</span>.<span class="pl-s1">tools</span>.<span class="pl-s1">mcp</span> <span class="pl-k">import</span> <span class="pl-v">MCPClient</span></td>
        </tr>
        <tr>
          <td id="file-simple_remote_mcp-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-simple_remote_mcp-py-LC3" class="blob-code blob-code-inner js-file-line"><span class="pl-k">from</span> <span class="pl-s1">mcp</span> <span class="pl-k">import</span> <span class="pl-s1">stdio_client</span>, <span class="pl-v">StdioServerParameters</span></td>
        </tr>
        <tr>
          <td id="file-simple_remote_mcp-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-simple_remote_mcp-py-LC4" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-simple_remote_mcp-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-simple_remote_mcp-py-LC5" class="blob-code blob-code-inner js-file-line"><span class="pl-s1">aws_docs_client</span> <span class="pl-c1">=</span> <span class="pl-en">MCPClient</span>(</td>
        </tr>
        <tr>
          <td id="file-simple_remote_mcp-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-simple_remote_mcp-py-LC6" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">lambda</span>: <span class="pl-en">stdio_client</span>(<span class="pl-en">StdioServerParameters</span>(<span class="pl-s1">command</span><span class="pl-c1">=</span><span class="pl-s">"uvx"</span>, <span class="pl-s1">args</span><span class="pl-c1">=</span>[<span class="pl-s">"awslabs.aws-documentation-mcp-server@latest"</span>]))</td>
        </tr>
        <tr>
          <td id="file-simple_remote_mcp-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-simple_remote_mcp-py-LC7" class="blob-code blob-code-inner js-file-line">)</td>
        </tr>
        <tr>
          <td id="file-simple_remote_mcp-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-simple_remote_mcp-py-LC8" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-simple_remote_mcp-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-simple_remote_mcp-py-LC9" class="blob-code blob-code-inner js-file-line"><span class="pl-k">with</span> <span class="pl-s1">aws_docs_client</span>:</td>
        </tr>
        <tr>
          <td id="file-simple_remote_mcp-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-simple_remote_mcp-py-LC10" class="blob-code blob-code-inner js-file-line">   <span class="pl-s1">agent</span> <span class="pl-c1">=</span> <span class="pl-en">Agent</span>(<span class="pl-s1">tools</span><span class="pl-c1">=</span><span class="pl-s1">aws_docs_client</span>.<span class="pl-c1">list_tools_sync</span>())</td>
        </tr>
        <tr>
          <td id="file-simple_remote_mcp-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-simple_remote_mcp-py-LC11" class="blob-code blob-code-inner js-file-line">   <span class="pl-s1">response</span> <span class="pl-c1">=</span> <span class="pl-en">agent</span>(<span class="pl-s">"Tell me about Amazon Bedrock and how to use it with Python"</span>)</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/3902874d7524c30e5be9d173a70ff13c/raw/28e7ea995a3a7a71ab2e3f7208fa5e841ee64501/simple_remote_mcp.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/3902874d7524c30e5be9d173a70ff13c#file-simple_remote_mcp-py" class="Link--inTextBlock">
          simple_remote_mcp.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>Here&#8217;s how it looks, note how it reads documentation, then decides to read further documentation. This can build a comprehensive understanding of AWS technologies, it&#8217;s pretty cool. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!jwGQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb632e0bd-861b-43d1-bbb8-1b4f347f5a87_3190x1008.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!jwGQ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb632e0bd-861b-43d1-bbb8-1b4f347f5a87_3190x1008.png 424w, https://substackcdn.com/image/fetch/$s_!jwGQ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb632e0bd-861b-43d1-bbb8-1b4f347f5a87_3190x1008.png 848w, https://substackcdn.com/image/fetch/$s_!jwGQ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb632e0bd-861b-43d1-bbb8-1b4f347f5a87_3190x1008.png 1272w, https://substackcdn.com/image/fetch/$s_!jwGQ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb632e0bd-861b-43d1-bbb8-1b4f347f5a87_3190x1008.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!jwGQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb632e0bd-861b-43d1-bbb8-1b4f347f5a87_3190x1008.png" width="1456" height="460" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b632e0bd-861b-43d1-bbb8-1b4f347f5a87_3190x1008.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:460,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:393945,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.letsdodevops.com/i/171701804?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb632e0bd-861b-43d1-bbb8-1b4f347f5a87_3190x1008.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!jwGQ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb632e0bd-861b-43d1-bbb8-1b4f347f5a87_3190x1008.png 424w, https://substackcdn.com/image/fetch/$s_!jwGQ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb632e0bd-861b-43d1-bbb8-1b4f347f5a87_3190x1008.png 848w, https://substackcdn.com/image/fetch/$s_!jwGQ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb632e0bd-861b-43d1-bbb8-1b4f347f5a87_3190x1008.png 1272w, https://substackcdn.com/image/fetch/$s_!jwGQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb632e0bd-861b-43d1-bbb8-1b4f347f5a87_3190x1008.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h1>Simple GitHub Loop</h1><p>While still simple, I added a few features to the single-MCP scripts. First, we use functions to define the MCPs. </p><p>You can see on line 6 that we define a function to build the streamable remote MCP client, just like the AWS documentation MCP. </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist140389479\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-simple_github_loop1-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;simple_github_loop1.py content, created by KyMidd on 01:06PM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;4\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;simple_github_loop1.py\&quot;>\n        <tr>\n          <td id=\&quot;file-simple_github_loop1-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-simple_github_loop1-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c># Imports...</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_github_loop1-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-simple_github_loop1-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c># User question to start</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_github_loop1-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-simple_github_loop1-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s1>user_question</span> <span class=pl-c1>=</span> <span class=pl-s>&amp;quot;Tell me about the Repos I own.&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_github_loop1-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-simple_github_loop1-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_github_loop1-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-simple_github_loop1-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c># Build GitHub MCP</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_github_loop1-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-simple_github_loop1-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-en>github_streamable_mcp_client</span>():</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_github_loop1-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-simple_github_loop1-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>token</span> <span class=pl-c1>=</span> <span class=pl-s1>os</span>.<span class=pl-c1>getenv</span>(<span class=pl-s>&amp;quot;GITHUB_TOKEN&amp;quot;</span>, <span class=pl-s>&amp;quot;&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_github_loop1-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-simple_github_loop1-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>if</span> <span class=pl-c1>not</span> <span class=pl-s1>token</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_github_loop1-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-simple_github_loop1-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>raise</span> <span class=pl-en>RuntimeError</span>(<span class=pl-s>&amp;quot;GITHUB_TOKEN is not set&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_github_loop1-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-simple_github_loop1-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>return</span> <span class=pl-en>MCPClient</span>(<span class=pl-k>lambda</span>: <span class=pl-en>streamablehttp_client</span>(</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_github_loop1-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-simple_github_loop1-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s>&amp;quot;https://api.githubcopilot.com/mcp/&amp;quot;</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_github_loop1-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-simple_github_loop1-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>headers</span><span class=pl-c1>=</span>{<span class=pl-s>&amp;quot;Authorization&amp;quot;</span>: <span class=pl-s>f&amp;quot;Bearer <span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>token</span><span class=pl-kos>}</span></span>&amp;quot;</span>}</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_github_loop1-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-simple_github_loop1-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    ))</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/898d3952a09dc0956452790ec1873576/raw/41f5b12d81d841a5d2e8a78195b907a481b40bca/simple_github_loop1.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/898d3952a09dc0956452790ec1873576#file-simple_github_loop1-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          simple_github_loop1.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-59543e005c9c.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-59543e005c9c.css"><div id="gist140389479" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-simple_github_loop1-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="4" data-paste-markdown-skip="" data-tagsearch-path="simple_github_loop1.py">
        <tbody><tr>
          <td id="file-simple_github_loop1-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-simple_github_loop1-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-c"># Imports...</span></td>
        </tr>
        <tr>
          <td id="file-simple_github_loop1-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-simple_github_loop1-py-LC2" class="blob-code blob-code-inner js-file-line"><span class="pl-c"># User question to start</span></td>
        </tr>
        <tr>
          <td id="file-simple_github_loop1-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-simple_github_loop1-py-LC3" class="blob-code blob-code-inner js-file-line"><span class="pl-s1">user_question</span> <span class="pl-c1">=</span> <span class="pl-s">"Tell me about the Repos I own."</span></td>
        </tr>
        <tr>
          <td id="file-simple_github_loop1-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-simple_github_loop1-py-LC4" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-simple_github_loop1-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-simple_github_loop1-py-LC5" class="blob-code blob-code-inner js-file-line"><span class="pl-c"># Build GitHub MCP</span></td>
        </tr>
        <tr>
          <td id="file-simple_github_loop1-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-simple_github_loop1-py-LC6" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-en">github_streamable_mcp_client</span>():</td>
        </tr>
        <tr>
          <td id="file-simple_github_loop1-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-simple_github_loop1-py-LC7" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">token</span> <span class="pl-c1">=</span> <span class="pl-s1">os</span>.<span class="pl-c1">getenv</span>(<span class="pl-s">"GITHUB_TOKEN"</span>, <span class="pl-s">""</span>)</td>
        </tr>
        <tr>
          <td id="file-simple_github_loop1-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-simple_github_loop1-py-LC8" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">if</span> <span class="pl-c1">not</span> <span class="pl-s1">token</span>:</td>
        </tr>
        <tr>
          <td id="file-simple_github_loop1-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-simple_github_loop1-py-LC9" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">raise</span> <span class="pl-en">RuntimeError</span>(<span class="pl-s">"GITHUB_TOKEN is not set"</span>)</td>
        </tr>
        <tr>
          <td id="file-simple_github_loop1-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-simple_github_loop1-py-LC10" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">return</span> <span class="pl-en">MCPClient</span>(<span class="pl-k">lambda</span>: <span class="pl-en">streamablehttp_client</span>(</td>
        </tr>
        <tr>
          <td id="file-simple_github_loop1-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-simple_github_loop1-py-LC11" class="blob-code blob-code-inner js-file-line">        <span class="pl-s">"https://api.githubcopilot.com/mcp/"</span>,</td>
        </tr>
        <tr>
          <td id="file-simple_github_loop1-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-simple_github_loop1-py-LC12" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">headers</span><span class="pl-c1">=</span>{<span class="pl-s">"Authorization"</span>: <span class="pl-s">f"Bearer <span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">token</span><span class="pl-kos">}</span></span>"</span>}</td>
        </tr>
        <tr>
          <td id="file-simple_github_loop1-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-simple_github_loop1-py-LC13" class="blob-code blob-code-inner js-file-line">    ))</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/898d3952a09dc0956452790ec1873576/raw/41f5b12d81d841a5d2e8a78195b907a481b40bca/simple_github_loop1.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/898d3952a09dc0956452790ec1873576#file-simple_github_loop1-py" class="Link--inTextBlock">
          simple_github_loop1.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>A bit lower down, we define the github MCP client, line 2. </p><p>And then on line 5, we connect to the MCP client on line 5 with the &#8220;with&#8221;. </p><p>On line 8, we establish the bedrock model, which customizes how the model works (number of tokens permitted, etc.), and build a list of tools we can pass to the agent, line 11-12. </p><p>Then on line 15 we build the agent and pass it the tools list. </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist140389696\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-simple_github_loop2-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;simple_github_loop2.py content, created by KyMidd on 01:23PM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;4\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;simple_github_loop2.py\&quot;>\n        <tr>\n          <td id=\&quot;file-simple_github_loop2-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-simple_github_loop2-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c># Create MCP clients</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_github_loop2-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-simple_github_loop2-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s1>github_mcp_client</span> <span class=pl-c1>=</span> <span class=pl-en>github_streamable_mcp_client</span>()</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_github_loop2-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-simple_github_loop2-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_github_loop2-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-simple_github_loop2-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c># Open tools and start chatting</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_github_loop2-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-simple_github_loop2-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>with</span> <span class=pl-s1>github_mcp_client</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_github_loop2-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-simple_github_loop2-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_github_loop2-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-simple_github_loop2-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Configure bedrock model</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_github_loop2-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-simple_github_loop2-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>bedrock_model</span> <span class=pl-c1>=</span> <span class=pl-en>configure_bedrock_model</span>()</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_github_loop2-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-simple_github_loop2-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_github_loop2-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-simple_github_loop2-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Inventory tools each MCP provides</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_github_loop2-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-simple_github_loop2-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>github_tools</span> <span class=pl-c1>=</span> <span class=pl-s1>github_mcp_client</span>.<span class=pl-c1>list_tools_sync</span>()</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_github_loop2-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-simple_github_loop2-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>agent_tools</span> <span class=pl-c1>=</span> [<span class=pl-s1>calculator</span>, <span class=pl-s1>current_time</span>, <span class=pl-s1>retrieve</span>, <span class=pl-s1>use_aws</span>] <span class=pl-c1>+</span> <span class=pl-s1>github_tools</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_github_loop2-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-simple_github_loop2-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_github_loop2-py-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-simple_github_loop2-py-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Create agent with tools</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_github_loop2-py-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-simple_github_loop2-py-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>agent</span> <span class=pl-c1>=</span> <span class=pl-en>create_agent</span>(<span class=pl-s1>agent_tools</span>)</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/5ca226e1b5db636d5fa995d7d7c18fdc/raw/c47ef3cca9fc30db8ddaf2d9bcebf6c9b5305b1f/simple_github_loop2.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/5ca226e1b5db636d5fa995d7d7c18fdc#file-simple_github_loop2-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          simple_github_loop2.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-59543e005c9c.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-59543e005c9c.css"><div id="gist140389696" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-simple_github_loop2-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="4" data-paste-markdown-skip="" data-tagsearch-path="simple_github_loop2.py">
        <tbody><tr>
          <td id="file-simple_github_loop2-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-simple_github_loop2-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-c"># Create MCP clients</span></td>
        </tr>
        <tr>
          <td id="file-simple_github_loop2-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-simple_github_loop2-py-LC2" class="blob-code blob-code-inner js-file-line"><span class="pl-s1">github_mcp_client</span> <span class="pl-c1">=</span> <span class="pl-en">github_streamable_mcp_client</span>()</td>
        </tr>
        <tr>
          <td id="file-simple_github_loop2-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-simple_github_loop2-py-LC3" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-simple_github_loop2-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-simple_github_loop2-py-LC4" class="blob-code blob-code-inner js-file-line"><span class="pl-c"># Open tools and start chatting</span></td>
        </tr>
        <tr>
          <td id="file-simple_github_loop2-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-simple_github_loop2-py-LC5" class="blob-code blob-code-inner js-file-line"><span class="pl-k">with</span> <span class="pl-s1">github_mcp_client</span>:</td>
        </tr>
        <tr>
          <td id="file-simple_github_loop2-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-simple_github_loop2-py-LC6" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-simple_github_loop2-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-simple_github_loop2-py-LC7" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Configure bedrock model</span></td>
        </tr>
        <tr>
          <td id="file-simple_github_loop2-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-simple_github_loop2-py-LC8" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">bedrock_model</span> <span class="pl-c1">=</span> <span class="pl-en">configure_bedrock_model</span>()</td>
        </tr>
        <tr>
          <td id="file-simple_github_loop2-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-simple_github_loop2-py-LC9" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-simple_github_loop2-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-simple_github_loop2-py-LC10" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Inventory tools each MCP provides</span></td>
        </tr>
        <tr>
          <td id="file-simple_github_loop2-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-simple_github_loop2-py-LC11" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">github_tools</span> <span class="pl-c1">=</span> <span class="pl-s1">github_mcp_client</span>.<span class="pl-c1">list_tools_sync</span>()</td>
        </tr>
        <tr>
          <td id="file-simple_github_loop2-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-simple_github_loop2-py-LC12" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">agent_tools</span> <span class="pl-c1">=</span> [<span class="pl-s1">calculator</span>, <span class="pl-s1">current_time</span>, <span class="pl-s1">retrieve</span>, <span class="pl-s1">use_aws</span>] <span class="pl-c1">+</span> <span class="pl-s1">github_tools</span></td>
        </tr>
        <tr>
          <td id="file-simple_github_loop2-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-simple_github_loop2-py-LC13" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-simple_github_loop2-py-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-simple_github_loop2-py-LC14" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Create agent with tools</span></td>
        </tr>
        <tr>
          <td id="file-simple_github_loop2-py-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-simple_github_loop2-py-LC15" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">agent</span> <span class="pl-c1">=</span> <span class="pl-en">create_agent</span>(<span class="pl-s1">agent_tools</span>)</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/5ca226e1b5db636d5fa995d7d7c18fdc/raw/c47ef3cca9fc30db8ddaf2d9bcebf6c9b5305b1f/simple_github_loop2.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/5ca226e1b5db636d5fa995d7d7c18fdc#file-simple_github_loop2-py" class="Link--inTextBlock">
          simple_github_loop2.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>Next, we start a &#8220;while&#8221; loop where we ask the agent our question that we defined above, to find all the Repos we own. The agent works agentically to get an answer, and then we hold on line 12, where the tool asks us if we want to continue. </p><p>We can type &#8220;exit&#8221; to break the loop and exit the script, or we can ask another question. Since the Agent is still instantiated, it remembers the previous question and information fetched from MCP, and can continue the conversation. </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist140389730\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-simple_github_loop3-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;simple_github_loop3.py content, created by KyMidd on 01:25PM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;4\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;simple_github_loop3.py\&quot;>\n        <tr>\n          <td id=\&quot;file-simple_github_loop3-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-simple_github_loop3-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>with</span> <span class=pl-s1>github_mcp_client</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_github_loop3-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-simple_github_loop3-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># ...</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_github_loop3-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-simple_github_loop3-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Begin conversation loop</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_github_loop3-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-simple_github_loop3-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>while</span> <span class=pl-c1>True</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_github_loop3-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-simple_github_loop3-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-en>print</span>(<span class=pl-s>&amp;quot;User question:&amp;quot;</span>, <span class=pl-s1>user_question</span>, <span class=pl-s1>end</span><span class=pl-c1>=</span><span class=pl-s>&amp;quot;<span class=pl-cce>\\n</span><span class=pl-cce>\\n</span>&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_github_loop3-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-simple_github_loop3-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_github_loop3-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-simple_github_loop3-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># Get response from agent</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_github_loop3-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-simple_github_loop3-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>response</span> <span class=pl-c1>=</span> <span class=pl-en>agent</span>(<span class=pl-s1>user_question</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_github_loop3-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-simple_github_loop3-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-en>print</span>(<span class=pl-s>&amp;quot;-&amp;quot;</span> <span class=pl-c1>*</span> <span class=pl-c1>50</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_github_loop3-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-simple_github_loop3-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_github_loop3-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-simple_github_loop3-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># Ask for follow-up question</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_github_loop3-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-simple_github_loop3-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>user_question</span> <span class=pl-c1>=</span> <span class=pl-en>get_user_input</span>(<span class=pl-s>&amp;quot;Question for model (or &amp;#39;exit&amp;#39; to exit): &amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_github_loop3-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-simple_github_loop3-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>if</span> <span class=pl-s1>user_question</span>.<span class=pl-c1>lower</span>() <span class=pl-c1>==</span> <span class=pl-s>&amp;#39;exit&amp;#39;</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_github_loop3-py-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-simple_github_loop3-py-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-k>break</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_github_loop3-py-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-simple_github_loop3-py-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-en>print</span>(<span class=pl-s>&amp;quot;-&amp;quot;</span> <span class=pl-c1>*</span> <span class=pl-c1>50</span>)</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/83a8feaeb9184bfab7fbe81552e073ea/raw/366341326d646078a386d4da8eedf8ec2d5676d5/simple_github_loop3.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/83a8feaeb9184bfab7fbe81552e073ea#file-simple_github_loop3-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          simple_github_loop3.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-59543e005c9c.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-59543e005c9c.css"><div id="gist140389730" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-simple_github_loop3-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="4" data-paste-markdown-skip="" data-tagsearch-path="simple_github_loop3.py">
        <tbody><tr>
          <td id="file-simple_github_loop3-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-simple_github_loop3-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">with</span> <span class="pl-s1">github_mcp_client</span>:</td>
        </tr>
        <tr>
          <td id="file-simple_github_loop3-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-simple_github_loop3-py-LC2" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># ...</span></td>
        </tr>
        <tr>
          <td id="file-simple_github_loop3-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-simple_github_loop3-py-LC3" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Begin conversation loop</span></td>
        </tr>
        <tr>
          <td id="file-simple_github_loop3-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-simple_github_loop3-py-LC4" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">while</span> <span class="pl-c1">True</span>:</td>
        </tr>
        <tr>
          <td id="file-simple_github_loop3-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-simple_github_loop3-py-LC5" class="blob-code blob-code-inner js-file-line">        <span class="pl-en">print</span>(<span class="pl-s">"User question:"</span>, <span class="pl-s1">user_question</span>, <span class="pl-s1">end</span><span class="pl-c1">=</span><span class="pl-s">"<span class="pl-cce">\n</span><span class="pl-cce">\n</span>"</span>)</td>
        </tr>
        <tr>
          <td id="file-simple_github_loop3-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-simple_github_loop3-py-LC6" class="blob-code blob-code-inner js-file-line">        </td>
        </tr>
        <tr>
          <td id="file-simple_github_loop3-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-simple_github_loop3-py-LC7" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># Get response from agent</span></td>
        </tr>
        <tr>
          <td id="file-simple_github_loop3-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-simple_github_loop3-py-LC8" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">response</span> <span class="pl-c1">=</span> <span class="pl-en">agent</span>(<span class="pl-s1">user_question</span>)</td>
        </tr>
        <tr>
          <td id="file-simple_github_loop3-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-simple_github_loop3-py-LC9" class="blob-code blob-code-inner js-file-line">        <span class="pl-en">print</span>(<span class="pl-s">"-"</span> <span class="pl-c1">*</span> <span class="pl-c1">50</span>)</td>
        </tr>
        <tr>
          <td id="file-simple_github_loop3-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-simple_github_loop3-py-LC10" class="blob-code blob-code-inner js-file-line">        </td>
        </tr>
        <tr>
          <td id="file-simple_github_loop3-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-simple_github_loop3-py-LC11" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># Ask for follow-up question</span></td>
        </tr>
        <tr>
          <td id="file-simple_github_loop3-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-simple_github_loop3-py-LC12" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">user_question</span> <span class="pl-c1">=</span> <span class="pl-en">get_user_input</span>(<span class="pl-s">"Question for model (or 'exit' to exit): "</span>)</td>
        </tr>
        <tr>
          <td id="file-simple_github_loop3-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-simple_github_loop3-py-LC13" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">if</span> <span class="pl-s1">user_question</span>.<span class="pl-c1">lower</span>() <span class="pl-c1">==</span> <span class="pl-s">'exit'</span>:</td>
        </tr>
        <tr>
          <td id="file-simple_github_loop3-py-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-simple_github_loop3-py-LC14" class="blob-code blob-code-inner js-file-line">            <span class="pl-k">break</span></td>
        </tr>
        <tr>
          <td id="file-simple_github_loop3-py-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-simple_github_loop3-py-LC15" class="blob-code blob-code-inner js-file-line">        <span class="pl-en">print</span>(<span class="pl-s">"-"</span> <span class="pl-c1">*</span> <span class="pl-c1">50</span>)</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/83a8feaeb9184bfab7fbe81552e073ea/raw/366341326d646078a386d4da8eedf8ec2d5676d5/simple_github_loop3.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/83a8feaeb9184bfab7fbe81552e073ea#file-simple_github_loop3-py" class="Link--inTextBlock">
          simple_github_loop3.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>Here&#8217;s how it looks. I think it&#8217;s super cool that it can figure out who <strong>I</strong> am, and then read stuff. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!f72H!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33e793c2-9f5f-4388-9a6b-d729149bbd40_2878x912.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!f72H!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33e793c2-9f5f-4388-9a6b-d729149bbd40_2878x912.png 424w, https://substackcdn.com/image/fetch/$s_!f72H!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33e793c2-9f5f-4388-9a6b-d729149bbd40_2878x912.png 848w, https://substackcdn.com/image/fetch/$s_!f72H!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33e793c2-9f5f-4388-9a6b-d729149bbd40_2878x912.png 1272w, https://substackcdn.com/image/fetch/$s_!f72H!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33e793c2-9f5f-4388-9a6b-d729149bbd40_2878x912.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!f72H!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33e793c2-9f5f-4388-9a6b-d729149bbd40_2878x912.png" width="1456" height="461" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/33e793c2-9f5f-4388-9a6b-d729149bbd40_2878x912.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:461,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:254191,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.letsdodevops.com/i/171701804?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33e793c2-9f5f-4388-9a6b-d729149bbd40_2878x912.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!f72H!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33e793c2-9f5f-4388-9a6b-d729149bbd40_2878x912.png 424w, https://substackcdn.com/image/fetch/$s_!f72H!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33e793c2-9f5f-4388-9a6b-d729149bbd40_2878x912.png 848w, https://substackcdn.com/image/fetch/$s_!f72H!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33e793c2-9f5f-4388-9a6b-d729149bbd40_2878x912.png 1272w, https://substackcdn.com/image/fetch/$s_!f72H!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33e793c2-9f5f-4388-9a6b-d729149bbd40_2878x912.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h1>Simple PagerDuty - Local MCP</h1><p>I&#8217;m a big fan of PagerDuty, and I see that they don&#8217;t yet have a remote hosted MCP for us to consume. However, they have <a href="https://github.com/PagerDuty/pagerduty-mcp-server">published a local MCP</a> that we can run ourselves. I staged that locally (and got uvx/uv installed), and then wrote this Strands script to use it. </p><p>On line 2 we establish our starter question, to find all active issues over the last 4 issues, and filter them. </p><p>Then on line 5 we define a function to utilize the local MCP server. Note that we&#8217;re not streaming anything from a remote destination, we&#8217;re literally providing the command flags that would start the MCP server if we ran them. </p><p>This script, when run, will basically call the MCP server as a child when we get to the &#8220;with&#8221; part of the function. </p><p>Note the &#8220;env&#8221; variables we inject into it on line 19-21. These are required to read PagerDuty. </p><blockquote><p>see line 17, where we can enable writing to pagerduty, and with it commented out, the MCP will only execute &#8220;read&#8221; operations, which I feel is much safer with the state of AI today. </p></blockquote><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist140389768\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-simple_pagerduty1-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;simple_pagerduty1.py content, created by KyMidd on 01:28PM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;4\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;simple_pagerduty1.py\&quot;>\n        <tr>\n          <td id=\&quot;file-simple_pagerduty1-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-simple_pagerduty1-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c># User question to start</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_pagerduty1-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-simple_pagerduty1-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s1>user_question</span> <span class=pl-c1>=</span> <span class=pl-s>&amp;quot;Find all active issues over the last 4 hours, find critical outages, and research potential code changes that caused those issues.&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_pagerduty1-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-simple_pagerduty1-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_pagerduty1-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-simple_pagerduty1-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c># Build PagerDuty MCP</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_pagerduty1-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-simple_pagerduty1-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-en>build_pagerduty_mcp_client</span>():</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_pagerduty1-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-simple_pagerduty1-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s>&amp;quot;&amp;quot;&amp;quot;Build and return a PagerDuty MCP client.&amp;quot;&amp;quot;&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_pagerduty1-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-simple_pagerduty1-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>return</span> <span class=pl-en>MCPClient</span>(<span class=pl-k>lambda</span>: <span class=pl-en>stdio_client</span>(</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_pagerduty1-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-simple_pagerduty1-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-en>StdioServerParameters</span>(</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_pagerduty1-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-simple_pagerduty1-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>command</span><span class=pl-c1>=</span><span class=pl-s>&amp;quot;uv&amp;quot;</span>, </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_pagerduty1-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-simple_pagerduty1-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>args</span><span class=pl-c1>=</span>[</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_pagerduty1-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-simple_pagerduty1-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                <span class=pl-s>&amp;quot;run&amp;quot;</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_pagerduty1-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-simple_pagerduty1-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                <span class=pl-s>&amp;quot;--directory&amp;quot;</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_pagerduty1-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-simple_pagerduty1-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                <span class=pl-s>&amp;quot;/Users/kyler/git/GitHub/PagerDuty/pagerduty-mcp-server&amp;quot;</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_pagerduty1-py-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-simple_pagerduty1-py-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                <span class=pl-s>&amp;quot;python&amp;quot;</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_pagerduty1-py-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-simple_pagerduty1-py-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                <span class=pl-s>&amp;quot;-m&amp;quot;</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_pagerduty1-py-L16\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;16\&quot;></td>\n          <td id=\&quot;file-simple_pagerduty1-py-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                <span class=pl-s>&amp;quot;pagerduty_mcp&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_pagerduty1-py-L17\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;17\&quot;></td>\n          <td id=\&quot;file-simple_pagerduty1-py-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                <span class=pl-c>#&amp;quot;--enable-write-tools&amp;quot;  # This flag enables write operations on the MCP Server enabling you to create issues, pull requests, etc.</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_pagerduty1-py-L18\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;18\&quot;></td>\n          <td id=\&quot;file-simple_pagerduty1-py-LC18\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            ],</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_pagerduty1-py-L19\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;19\&quot;></td>\n          <td id=\&quot;file-simple_pagerduty1-py-LC19\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>env</span><span class=pl-c1>=</span>{</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_pagerduty1-py-L20\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;20\&quot;></td>\n          <td id=\&quot;file-simple_pagerduty1-py-LC20\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                <span class=pl-s>&amp;quot;PAGERDUTY_HOST&amp;quot;</span>: <span class=pl-s>&amp;quot;https://api.my_company.pagerduty.com&amp;quot;</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_pagerduty1-py-L21\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;21\&quot;></td>\n          <td id=\&quot;file-simple_pagerduty1-py-LC21\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                <span class=pl-s>&amp;quot;PAGERDUTY_USER_API_KEY&amp;quot;</span>: <span class=pl-s1>os</span>.<span class=pl-c1>getenv</span>(<span class=pl-s>&amp;quot;PAGERDUTY_USER_API_KEY&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_pagerduty1-py-L22\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;22\&quot;></td>\n          <td id=\&quot;file-simple_pagerduty1-py-LC22\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            },</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_pagerduty1-py-L23\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;23\&quot;></td>\n          <td id=\&quot;file-simple_pagerduty1-py-LC23\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        )</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_pagerduty1-py-L24\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;24\&quot;></td>\n          <td id=\&quot;file-simple_pagerduty1-py-LC24\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    ))</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/4deae867281f139acfdd2d89f5df433a/raw/6f250c73b66e4c07f8c26714a2b2d91b1bdd6302/simple_pagerduty1.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/4deae867281f139acfdd2d89f5df433a#file-simple_pagerduty1-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          simple_pagerduty1.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-59543e005c9c.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-59543e005c9c.css"><div id="gist140389768" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-simple_pagerduty1-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="4" data-paste-markdown-skip="" data-tagsearch-path="simple_pagerduty1.py">
        <tbody><tr>
          <td id="file-simple_pagerduty1-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-simple_pagerduty1-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-c"># User question to start</span></td>
        </tr>
        <tr>
          <td id="file-simple_pagerduty1-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-simple_pagerduty1-py-LC2" class="blob-code blob-code-inner js-file-line"><span class="pl-s1">user_question</span> <span class="pl-c1">=</span> <span class="pl-s">"Find all active issues over the last 4 hours, find critical outages, and research potential code changes that caused those issues."</span></td>
        </tr>
        <tr>
          <td id="file-simple_pagerduty1-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-simple_pagerduty1-py-LC3" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-simple_pagerduty1-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-simple_pagerduty1-py-LC4" class="blob-code blob-code-inner js-file-line"><span class="pl-c"># Build PagerDuty MCP</span></td>
        </tr>
        <tr>
          <td id="file-simple_pagerduty1-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-simple_pagerduty1-py-LC5" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-en">build_pagerduty_mcp_client</span>():</td>
        </tr>
        <tr>
          <td id="file-simple_pagerduty1-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-simple_pagerduty1-py-LC6" class="blob-code blob-code-inner js-file-line">    <span class="pl-s">"""Build and return a PagerDuty MCP client."""</span></td>
        </tr>
        <tr>
          <td id="file-simple_pagerduty1-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-simple_pagerduty1-py-LC7" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">return</span> <span class="pl-en">MCPClient</span>(<span class="pl-k">lambda</span>: <span class="pl-en">stdio_client</span>(</td>
        </tr>
        <tr>
          <td id="file-simple_pagerduty1-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-simple_pagerduty1-py-LC8" class="blob-code blob-code-inner js-file-line">        <span class="pl-en">StdioServerParameters</span>(</td>
        </tr>
        <tr>
          <td id="file-simple_pagerduty1-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-simple_pagerduty1-py-LC9" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">command</span><span class="pl-c1">=</span><span class="pl-s">"uv"</span>, </td>
        </tr>
        <tr>
          <td id="file-simple_pagerduty1-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-simple_pagerduty1-py-LC10" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">args</span><span class="pl-c1">=</span>[</td>
        </tr>
        <tr>
          <td id="file-simple_pagerduty1-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-simple_pagerduty1-py-LC11" class="blob-code blob-code-inner js-file-line">                <span class="pl-s">"run"</span>,</td>
        </tr>
        <tr>
          <td id="file-simple_pagerduty1-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-simple_pagerduty1-py-LC12" class="blob-code blob-code-inner js-file-line">                <span class="pl-s">"--directory"</span>,</td>
        </tr>
        <tr>
          <td id="file-simple_pagerduty1-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-simple_pagerduty1-py-LC13" class="blob-code blob-code-inner js-file-line">                <span class="pl-s">"/Users/kyler/git/GitHub/PagerDuty/pagerduty-mcp-server"</span>,</td>
        </tr>
        <tr>
          <td id="file-simple_pagerduty1-py-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-simple_pagerduty1-py-LC14" class="blob-code blob-code-inner js-file-line">                <span class="pl-s">"python"</span>,</td>
        </tr>
        <tr>
          <td id="file-simple_pagerduty1-py-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-simple_pagerduty1-py-LC15" class="blob-code blob-code-inner js-file-line">                <span class="pl-s">"-m"</span>,</td>
        </tr>
        <tr>
          <td id="file-simple_pagerduty1-py-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-simple_pagerduty1-py-LC16" class="blob-code blob-code-inner js-file-line">                <span class="pl-s">"pagerduty_mcp"</span></td>
        </tr>
        <tr>
          <td id="file-simple_pagerduty1-py-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-simple_pagerduty1-py-LC17" class="blob-code blob-code-inner js-file-line">                <span class="pl-c">#"--enable-write-tools"  # This flag enables write operations on the MCP Server enabling you to create issues, pull requests, etc.</span></td>
        </tr>
        <tr>
          <td id="file-simple_pagerduty1-py-L18" class="blob-num js-line-number js-blob-rnum" data-line-number="18"></td>
          <td id="file-simple_pagerduty1-py-LC18" class="blob-code blob-code-inner js-file-line">            ],</td>
        </tr>
        <tr>
          <td id="file-simple_pagerduty1-py-L19" class="blob-num js-line-number js-blob-rnum" data-line-number="19"></td>
          <td id="file-simple_pagerduty1-py-LC19" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">env</span><span class="pl-c1">=</span>{</td>
        </tr>
        <tr>
          <td id="file-simple_pagerduty1-py-L20" class="blob-num js-line-number js-blob-rnum" data-line-number="20"></td>
          <td id="file-simple_pagerduty1-py-LC20" class="blob-code blob-code-inner js-file-line">                <span class="pl-s">"PAGERDUTY_HOST"</span>: <span class="pl-s">"https://api.my_company.pagerduty.com"</span>,</td>
        </tr>
        <tr>
          <td id="file-simple_pagerduty1-py-L21" class="blob-num js-line-number js-blob-rnum" data-line-number="21"></td>
          <td id="file-simple_pagerduty1-py-LC21" class="blob-code blob-code-inner js-file-line">                <span class="pl-s">"PAGERDUTY_USER_API_KEY"</span>: <span class="pl-s1">os</span>.<span class="pl-c1">getenv</span>(<span class="pl-s">"PAGERDUTY_USER_API_KEY"</span>)</td>
        </tr>
        <tr>
          <td id="file-simple_pagerduty1-py-L22" class="blob-num js-line-number js-blob-rnum" data-line-number="22"></td>
          <td id="file-simple_pagerduty1-py-LC22" class="blob-code blob-code-inner js-file-line">            },</td>
        </tr>
        <tr>
          <td id="file-simple_pagerduty1-py-L23" class="blob-num js-line-number js-blob-rnum" data-line-number="23"></td>
          <td id="file-simple_pagerduty1-py-LC23" class="blob-code blob-code-inner js-file-line">        )</td>
        </tr>
        <tr>
          <td id="file-simple_pagerduty1-py-L24" class="blob-num js-line-number js-blob-rnum" data-line-number="24"></td>
          <td id="file-simple_pagerduty1-py-LC24" class="blob-code blob-code-inner js-file-line">    ))</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/4deae867281f139acfdd2d89f5df433a/raw/6f250c73b66e4c07f8c26714a2b2d91b1bdd6302/simple_pagerduty1.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/4deae867281f139acfdd2d89f5df433a#file-simple_pagerduty1-py" class="Link--inTextBlock">
          simple_pagerduty1.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>The &#8220;with&#8221; and other script info is exactly the same as the above, so skipping it, you can read the <a href="https://github.com/KyMidd/StrandsAgenticExamples/blob/master/simple_pagerduty.py">simple_pagerduty.py</a> script if you want to see what we&#8217;re doing there. </p><p>Here&#8217;s how it looks: </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!WEKI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F636dccdb-12af-4add-b667-bd0788719754_3292x1206.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!WEKI!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F636dccdb-12af-4add-b667-bd0788719754_3292x1206.png 424w, https://substackcdn.com/image/fetch/$s_!WEKI!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F636dccdb-12af-4add-b667-bd0788719754_3292x1206.png 848w, https://substackcdn.com/image/fetch/$s_!WEKI!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F636dccdb-12af-4add-b667-bd0788719754_3292x1206.png 1272w, https://substackcdn.com/image/fetch/$s_!WEKI!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F636dccdb-12af-4add-b667-bd0788719754_3292x1206.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!WEKI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F636dccdb-12af-4add-b667-bd0788719754_3292x1206.png" width="1456" height="533" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/636dccdb-12af-4add-b667-bd0788719754_3292x1206.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:533,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:327935,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.letsdodevops.com/i/171701804?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F636dccdb-12af-4add-b667-bd0788719754_3292x1206.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!WEKI!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F636dccdb-12af-4add-b667-bd0788719754_3292x1206.png 424w, https://substackcdn.com/image/fetch/$s_!WEKI!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F636dccdb-12af-4add-b667-bd0788719754_3292x1206.png 848w, https://substackcdn.com/image/fetch/$s_!WEKI!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F636dccdb-12af-4add-b667-bd0788719754_3292x1206.png 1272w, https://substackcdn.com/image/fetch/$s_!WEKI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F636dccdb-12af-4add-b667-bd0788719754_3292x1206.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h1>Simple AWS</h1><p>There is a local MCP in preview that permits the agent to run AWS commands, but it&#8217;s in beta, and not very easy to run. However, Strands has a built-in tool that permits it to do things in AWS, able to do whatever the boto3 library is capable of doing. </p><p>It&#8217;s called &#8220;use_aws&#8221;. We import it on line 3, along with a few other built-in tools. </p><p>You can see on line 8 we pass that same list of built-in tools as a list to our agent on line 11. </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist140389850\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-simple_aws-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;simple_aws.py content, created by KyMidd on 01:34PM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;4\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;simple_aws.py\&quot;>\n        <tr>\n          <td id=\&quot;file-simple_aws-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-simple_aws-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>from</span> <span class=pl-s1>strands</span> <span class=pl-k>import</span> <span class=pl-v>Agent</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_aws-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-simple_aws-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>from</span> <span class=pl-s1>strands</span>.<span class=pl-s1>models</span> <span class=pl-k>import</span> <span class=pl-v>BedrockModel</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_aws-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-simple_aws-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>from</span> <span class=pl-s1>strands_tools</span> <span class=pl-k>import</span> <span class=pl-s1>calculator</span>, <span class=pl-s1>current_time</span>, <span class=pl-s1>retrieve</span>, <span class=pl-s1>use_aws</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_aws-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-simple_aws-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c># Lines skipped</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_aws-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-simple_aws-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c># Configure bedrock model</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_aws-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-simple_aws-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s1>bedrock_model</span> <span class=pl-c1>=</span> <span class=pl-en>configure_bedrock_model</span>()</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_aws-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-simple_aws-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_aws-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-simple_aws-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s1>agent_tools</span> <span class=pl-c1>=</span> [<span class=pl-s1>calculator</span>, <span class=pl-s1>current_time</span>, <span class=pl-s1>retrieve</span>, <span class=pl-s1>use_aws</span>]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_aws-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-simple_aws-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_aws-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-simple_aws-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c># Create agent with tools</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_aws-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-simple_aws-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s1>agent</span> <span class=pl-c1>=</span> <span class=pl-en>create_agent</span>(<span class=pl-s1>agent_tools</span>)</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/6c871bb8d87cc0f143c60b6acba40eb6/raw/e90e7b99a0f34e6484bf03be95c581bfe2a93649/simple_aws.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/6c871bb8d87cc0f143c60b6acba40eb6#file-simple_aws-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          simple_aws.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-59543e005c9c.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-59543e005c9c.css"><div id="gist140389850" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-simple_aws-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="4" data-paste-markdown-skip="" data-tagsearch-path="simple_aws.py">
        <tbody><tr>
          <td id="file-simple_aws-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-simple_aws-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">from</span> <span class="pl-s1">strands</span> <span class="pl-k">import</span> <span class="pl-v">Agent</span></td>
        </tr>
        <tr>
          <td id="file-simple_aws-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-simple_aws-py-LC2" class="blob-code blob-code-inner js-file-line"><span class="pl-k">from</span> <span class="pl-s1">strands</span>.<span class="pl-s1">models</span> <span class="pl-k">import</span> <span class="pl-v">BedrockModel</span></td>
        </tr>
        <tr>
          <td id="file-simple_aws-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-simple_aws-py-LC3" class="blob-code blob-code-inner js-file-line"><span class="pl-k">from</span> <span class="pl-s1">strands_tools</span> <span class="pl-k">import</span> <span class="pl-s1">calculator</span>, <span class="pl-s1">current_time</span>, <span class="pl-s1">retrieve</span>, <span class="pl-s1">use_aws</span></td>
        </tr>
        <tr>
          <td id="file-simple_aws-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-simple_aws-py-LC4" class="blob-code blob-code-inner js-file-line"><span class="pl-c"># Lines skipped</span></td>
        </tr>
        <tr>
          <td id="file-simple_aws-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-simple_aws-py-LC5" class="blob-code blob-code-inner js-file-line"><span class="pl-c"># Configure bedrock model</span></td>
        </tr>
        <tr>
          <td id="file-simple_aws-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-simple_aws-py-LC6" class="blob-code blob-code-inner js-file-line"><span class="pl-s1">bedrock_model</span> <span class="pl-c1">=</span> <span class="pl-en">configure_bedrock_model</span>()</td>
        </tr>
        <tr>
          <td id="file-simple_aws-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-simple_aws-py-LC7" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-simple_aws-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-simple_aws-py-LC8" class="blob-code blob-code-inner js-file-line"><span class="pl-s1">agent_tools</span> <span class="pl-c1">=</span> [<span class="pl-s1">calculator</span>, <span class="pl-s1">current_time</span>, <span class="pl-s1">retrieve</span>, <span class="pl-s1">use_aws</span>]</td>
        </tr>
        <tr>
          <td id="file-simple_aws-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-simple_aws-py-LC9" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-simple_aws-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-simple_aws-py-LC10" class="blob-code blob-code-inner js-file-line"><span class="pl-c"># Create agent with tools</span></td>
        </tr>
        <tr>
          <td id="file-simple_aws-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-simple_aws-py-LC11" class="blob-code blob-code-inner js-file-line"><span class="pl-s1">agent</span> <span class="pl-c1">=</span> <span class="pl-en">create_agent</span>(<span class="pl-s1">agent_tools</span>)</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/6c871bb8d87cc0f143c60b6acba40eb6/raw/e90e7b99a0f34e6484bf03be95c581bfe2a93649/simple_aws.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/6c871bb8d87cc0f143c60b6acba40eb6#file-simple_aws-py" class="Link--inTextBlock">
          simple_aws.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>Here&#8217;s how it looks: </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!A6xf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5fb6eb28-4fe5-480f-a6fc-03378f113cb3_2272x822.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!A6xf!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5fb6eb28-4fe5-480f-a6fc-03378f113cb3_2272x822.png 424w, https://substackcdn.com/image/fetch/$s_!A6xf!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5fb6eb28-4fe5-480f-a6fc-03378f113cb3_2272x822.png 848w, https://substackcdn.com/image/fetch/$s_!A6xf!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5fb6eb28-4fe5-480f-a6fc-03378f113cb3_2272x822.png 1272w, https://substackcdn.com/image/fetch/$s_!A6xf!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5fb6eb28-4fe5-480f-a6fc-03378f113cb3_2272x822.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!A6xf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5fb6eb28-4fe5-480f-a6fc-03378f113cb3_2272x822.png" width="1456" height="527" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5fb6eb28-4fe5-480f-a6fc-03378f113cb3_2272x822.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:527,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:184045,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.letsdodevops.com/i/171701804?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5fb6eb28-4fe5-480f-a6fc-03378f113cb3_2272x822.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!A6xf!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5fb6eb28-4fe5-480f-a6fc-03378f113cb3_2272x822.png 424w, https://substackcdn.com/image/fetch/$s_!A6xf!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5fb6eb28-4fe5-480f-a6fc-03378f113cb3_2272x822.png 848w, https://substackcdn.com/image/fetch/$s_!A6xf!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5fb6eb28-4fe5-480f-a6fc-03378f113cb3_2272x822.png 1272w, https://substackcdn.com/image/fetch/$s_!A6xf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5fb6eb28-4fe5-480f-a6fc-03378f113cb3_2272x822.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h1>Simple AWS Bedrock Knowledge Base</h1><p>I built a pretty extensive Knowledge Base on Bedrock for Vera - a GenAI bot that doesn&#8217;t operate agentically. So when I read that Strands permits it to talk to Bedrock I expected I&#8217;d have to build a ton of code, and re-ranking, and such. </p><p>However, it&#8217;s incredibly easy. I had to export the ID of the knowledge base: </p><pre><code>export KNOWLEDGE_BASE_ID=xxxxxx</code></pre><p>And then I had to import the &#8220;retrieve&#8221; tool, which retrieves information from Bedrock knowledge bases. I pass it like any other tool to the agent, and we&#8217;re in business. </p><blockquote><p>Notably, I already have AWS creds exported into this terminal, to utilize bedrock AI services, so I&#8217;m already authenticated. If you&#8217;re using a different back-end for AI services, make sure to also export AWS creds that can read the Bedrock KB. </p></blockquote><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist140389920\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-simple_aws_kb-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;simple_aws_kb.py content, created by KyMidd on 01:40PM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;4\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;simple_aws_kb.py\&quot;>\n        <tr>\n          <td id=\&quot;file-simple_aws_kb-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-simple_aws_kb-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>from</span> <span class=pl-s1>strands</span> <span class=pl-k>import</span> <span class=pl-v>Agent</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_aws_kb-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-simple_aws_kb-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>from</span> <span class=pl-s1>strands_tools</span> <span class=pl-k>import</span> <span class=pl-s1>retrieve</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_aws_kb-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-simple_aws_kb-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s1>agent</span> <span class=pl-c1>=</span> <span class=pl-en>Agent</span>(<span class=pl-s1>tools</span><span class=pl-c1>=</span>[<span class=pl-s1>retrieve</span>])</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-simple_aws_kb-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-simple_aws_kb-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-en>agent</span>(<span class=pl-s>&amp;quot;Tell me about the cloud platform team&amp;quot;</span>)</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/a003d9281384851f85b9216b3f84de15/raw/f287023d1f4bba85e71caca0ba59e10fd47a3630/simple_aws_kb.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/a003d9281384851f85b9216b3f84de15#file-simple_aws_kb-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          simple_aws_kb.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-59543e005c9c.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-59543e005c9c.css"><div id="gist140389920" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-simple_aws_kb-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="4" data-paste-markdown-skip="" data-tagsearch-path="simple_aws_kb.py">
        <tbody><tr>
          <td id="file-simple_aws_kb-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-simple_aws_kb-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">from</span> <span class="pl-s1">strands</span> <span class="pl-k">import</span> <span class="pl-v">Agent</span></td>
        </tr>
        <tr>
          <td id="file-simple_aws_kb-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-simple_aws_kb-py-LC2" class="blob-code blob-code-inner js-file-line"><span class="pl-k">from</span> <span class="pl-s1">strands_tools</span> <span class="pl-k">import</span> <span class="pl-s1">retrieve</span></td>
        </tr>
        <tr>
          <td id="file-simple_aws_kb-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-simple_aws_kb-py-LC3" class="blob-code blob-code-inner js-file-line"><span class="pl-s1">agent</span> <span class="pl-c1">=</span> <span class="pl-en">Agent</span>(<span class="pl-s1">tools</span><span class="pl-c1">=</span>[<span class="pl-s1">retrieve</span>])</td>
        </tr>
        <tr>
          <td id="file-simple_aws_kb-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-simple_aws_kb-py-LC4" class="blob-code blob-code-inner js-file-line"><span class="pl-en">agent</span>(<span class="pl-s">"Tell me about the cloud platform team"</span>)</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/a003d9281384851f85b9216b3f84de15/raw/f287023d1f4bba85e71caca0ba59e10fd47a3630/simple_aws_kb.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/a003d9281384851f85b9216b3f84de15#file-simple_aws_kb-py" class="Link--inTextBlock">
          simple_aws_kb.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>Here&#8217;s how it looks: </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!VUlp!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F507887a5-0938-4bba-9efa-2cbd132a50c8_3206x924.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!VUlp!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F507887a5-0938-4bba-9efa-2cbd132a50c8_3206x924.png 424w, https://substackcdn.com/image/fetch/$s_!VUlp!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F507887a5-0938-4bba-9efa-2cbd132a50c8_3206x924.png 848w, https://substackcdn.com/image/fetch/$s_!VUlp!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F507887a5-0938-4bba-9efa-2cbd132a50c8_3206x924.png 1272w, https://substackcdn.com/image/fetch/$s_!VUlp!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F507887a5-0938-4bba-9efa-2cbd132a50c8_3206x924.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!VUlp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F507887a5-0938-4bba-9efa-2cbd132a50c8_3206x924.png" width="1456" height="420" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/507887a5-0938-4bba-9efa-2cbd132a50c8_3206x924.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:420,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:251484,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.letsdodevops.com/i/171701804?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F507887a5-0938-4bba-9efa-2cbd132a50c8_3206x924.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!VUlp!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F507887a5-0938-4bba-9efa-2cbd132a50c8_3206x924.png 424w, https://substackcdn.com/image/fetch/$s_!VUlp!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F507887a5-0938-4bba-9efa-2cbd132a50c8_3206x924.png 848w, https://substackcdn.com/image/fetch/$s_!VUlp!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F507887a5-0938-4bba-9efa-2cbd132a50c8_3206x924.png 1272w, https://substackcdn.com/image/fetch/$s_!VUlp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F507887a5-0938-4bba-9efa-2cbd132a50c8_3206x924.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h1>Combined MCP</h1><p>The real power of MCP comes when you combine multiple MCP tools across platforms, because the model can correlate, say, outages (Pagerduty) to specific code changes (GitHub). </p><p>This is pretty intuitive once you&#8217;ve seen it. I&#8217;ve skipped some code that we covered above where we built the MCP function definitions. </p><p>On line 2-3, we call those MCPs, and on line 6 we do a &#8220;with&#8221; to open each of them. </p><p>Then on line 12-13, we sync the tools available for both, and on line 16, we build our &#8220;tool_belt&#8221; (haha), to combine the list of all the tools together. </p><p>And then on line 19 we instantiate the agent and pass it all the tools. </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist140389970\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-combined_mcp-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;combined_mcp.py content, created by KyMidd on 01:43PM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;4\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;combined_mcp.py\&quot;>\n        <tr>\n          <td id=\&quot;file-combined_mcp-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-combined_mcp-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c># Create MCP clients</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-combined_mcp-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-combined_mcp-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s1>github_mcp_client</span> <span class=pl-c1>=</span> <span class=pl-en>github_streamable_mcp_client</span>()</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-combined_mcp-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-combined_mcp-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s1>pagerduty_mcp_client</span> <span class=pl-c1>=</span> <span class=pl-en>build_pagerduty_mcp_client</span>()</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-combined_mcp-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-combined_mcp-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-combined_mcp-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-combined_mcp-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c># Open tools and start chatting</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-combined_mcp-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-combined_mcp-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>with</span> <span class=pl-s1>github_mcp_client</span>, <span class=pl-s1>pagerduty_mcp_client</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-combined_mcp-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-combined_mcp-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-combined_mcp-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-combined_mcp-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Configure bedrock model</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-combined_mcp-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-combined_mcp-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>bedrock_model</span> <span class=pl-c1>=</span> <span class=pl-en>configure_bedrock_model</span>()</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-combined_mcp-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-combined_mcp-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-combined_mcp-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-combined_mcp-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Inventory tools each MCP provides</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-combined_mcp-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-combined_mcp-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>github_tools</span> <span class=pl-c1>=</span> <span class=pl-s1>github_mcp_client</span>.<span class=pl-c1>list_tools_sync</span>()</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-combined_mcp-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-combined_mcp-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>pagerduty_tools</span> <span class=pl-c1>=</span> <span class=pl-s1>pagerduty_mcp_client</span>.<span class=pl-c1>list_tools_sync</span>()</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-combined_mcp-py-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-combined_mcp-py-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-combined_mcp-py-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-combined_mcp-py-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Build &amp;quot;tool belt&amp;quot; lol</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-combined_mcp-py-L16\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;16\&quot;></td>\n          <td id=\&quot;file-combined_mcp-py-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>agent_tools</span> <span class=pl-c1>=</span> <span class=pl-s1>github_tools</span> <span class=pl-c1>+</span> <span class=pl-s1>pagerduty_tools</span> <span class=pl-c1>+</span> [<span class=pl-s1>calculator</span>, <span class=pl-s1>current_time</span>, <span class=pl-s1>retrieve</span>, <span class=pl-s1>use_aws</span>]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-combined_mcp-py-L17\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;17\&quot;></td>\n          <td id=\&quot;file-combined_mcp-py-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-combined_mcp-py-L18\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;18\&quot;></td>\n          <td id=\&quot;file-combined_mcp-py-LC18\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Create agent with tools</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-combined_mcp-py-L19\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;19\&quot;></td>\n          <td id=\&quot;file-combined_mcp-py-LC19\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>agent</span> <span class=pl-c1>=</span> <span class=pl-en>create_agent</span>(<span class=pl-s1>agent_tools</span>)</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/a6326cdfc0b01cfa17e4dc56152431af/raw/03c2e0e1c81801f14ee563b6e1ba3d2110f93444/combined_mcp.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/a6326cdfc0b01cfa17e4dc56152431af#file-combined_mcp-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          combined_mcp.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-59543e005c9c.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-59543e005c9c.css"><div id="gist140389970" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-combined_mcp-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="4" data-paste-markdown-skip="" data-tagsearch-path="combined_mcp.py">
        <tbody><tr>
          <td id="file-combined_mcp-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-combined_mcp-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-c"># Create MCP clients</span></td>
        </tr>
        <tr>
          <td id="file-combined_mcp-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-combined_mcp-py-LC2" class="blob-code blob-code-inner js-file-line"><span class="pl-s1">github_mcp_client</span> <span class="pl-c1">=</span> <span class="pl-en">github_streamable_mcp_client</span>()</td>
        </tr>
        <tr>
          <td id="file-combined_mcp-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-combined_mcp-py-LC3" class="blob-code blob-code-inner js-file-line"><span class="pl-s1">pagerduty_mcp_client</span> <span class="pl-c1">=</span> <span class="pl-en">build_pagerduty_mcp_client</span>()</td>
        </tr>
        <tr>
          <td id="file-combined_mcp-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-combined_mcp-py-LC4" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-combined_mcp-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-combined_mcp-py-LC5" class="blob-code blob-code-inner js-file-line"><span class="pl-c"># Open tools and start chatting</span></td>
        </tr>
        <tr>
          <td id="file-combined_mcp-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-combined_mcp-py-LC6" class="blob-code blob-code-inner js-file-line"><span class="pl-k">with</span> <span class="pl-s1">github_mcp_client</span>, <span class="pl-s1">pagerduty_mcp_client</span>:</td>
        </tr>
        <tr>
          <td id="file-combined_mcp-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-combined_mcp-py-LC7" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-combined_mcp-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-combined_mcp-py-LC8" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Configure bedrock model</span></td>
        </tr>
        <tr>
          <td id="file-combined_mcp-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-combined_mcp-py-LC9" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">bedrock_model</span> <span class="pl-c1">=</span> <span class="pl-en">configure_bedrock_model</span>()</td>
        </tr>
        <tr>
          <td id="file-combined_mcp-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-combined_mcp-py-LC10" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-combined_mcp-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-combined_mcp-py-LC11" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Inventory tools each MCP provides</span></td>
        </tr>
        <tr>
          <td id="file-combined_mcp-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-combined_mcp-py-LC12" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">github_tools</span> <span class="pl-c1">=</span> <span class="pl-s1">github_mcp_client</span>.<span class="pl-c1">list_tools_sync</span>()</td>
        </tr>
        <tr>
          <td id="file-combined_mcp-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-combined_mcp-py-LC13" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">pagerduty_tools</span> <span class="pl-c1">=</span> <span class="pl-s1">pagerduty_mcp_client</span>.<span class="pl-c1">list_tools_sync</span>()</td>
        </tr>
        <tr>
          <td id="file-combined_mcp-py-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-combined_mcp-py-LC14" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-combined_mcp-py-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-combined_mcp-py-LC15" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Build "tool belt" lol</span></td>
        </tr>
        <tr>
          <td id="file-combined_mcp-py-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-combined_mcp-py-LC16" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">agent_tools</span> <span class="pl-c1">=</span> <span class="pl-s1">github_tools</span> <span class="pl-c1">+</span> <span class="pl-s1">pagerduty_tools</span> <span class="pl-c1">+</span> [<span class="pl-s1">calculator</span>, <span class="pl-s1">current_time</span>, <span class="pl-s1">retrieve</span>, <span class="pl-s1">use_aws</span>]</td>
        </tr>
        <tr>
          <td id="file-combined_mcp-py-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-combined_mcp-py-LC17" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-combined_mcp-py-L18" class="blob-num js-line-number js-blob-rnum" data-line-number="18"></td>
          <td id="file-combined_mcp-py-LC18" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Create agent with tools</span></td>
        </tr>
        <tr>
          <td id="file-combined_mcp-py-L19" class="blob-num js-line-number js-blob-rnum" data-line-number="19"></td>
          <td id="file-combined_mcp-py-LC19" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">agent</span> <span class="pl-c1">=</span> <span class="pl-en">create_agent</span>(<span class="pl-s1">agent_tools</span>)</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/a6326cdfc0b01cfa17e4dc56152431af/raw/03c2e0e1c81801f14ee563b6e1ba3d2110f93444/combined_mcp.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/a6326cdfc0b01cfa17e4dc56152431af#file-combined_mcp-py" class="Link--inTextBlock">
          combined_mcp.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>Here&#8217;s how it looks: </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!4IbS!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe7e764e-181d-41b2-ba3d-b508a2b1313b_3280x938.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!4IbS!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe7e764e-181d-41b2-ba3d-b508a2b1313b_3280x938.png 424w, https://substackcdn.com/image/fetch/$s_!4IbS!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe7e764e-181d-41b2-ba3d-b508a2b1313b_3280x938.png 848w, https://substackcdn.com/image/fetch/$s_!4IbS!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe7e764e-181d-41b2-ba3d-b508a2b1313b_3280x938.png 1272w, https://substackcdn.com/image/fetch/$s_!4IbS!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe7e764e-181d-41b2-ba3d-b508a2b1313b_3280x938.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!4IbS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe7e764e-181d-41b2-ba3d-b508a2b1313b_3280x938.png" width="1456" height="416" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fe7e764e-181d-41b2-ba3d-b508a2b1313b_3280x938.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:416,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:273195,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.letsdodevops.com/i/171701804?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe7e764e-181d-41b2-ba3d-b508a2b1313b_3280x938.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!4IbS!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe7e764e-181d-41b2-ba3d-b508a2b1313b_3280x938.png 424w, https://substackcdn.com/image/fetch/$s_!4IbS!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe7e764e-181d-41b2-ba3d-b508a2b1313b_3280x938.png 848w, https://substackcdn.com/image/fetch/$s_!4IbS!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe7e764e-181d-41b2-ba3d-b508a2b1313b_3280x938.png 1272w, https://substackcdn.com/image/fetch/$s_!4IbS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe7e764e-181d-41b2-ba3d-b508a2b1313b_3280x938.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h1>Summary</h1><p>In this article we&#8217;ve covered how to build Strands agentic Agents with python. We&#8217;ve talked about how different types of MCPs work, and built them, and finished up with a multi-platform bot that&#8217;s able to: </p><ul><li><p>Talk to PagerDuty to read your incidents</p></li><li><p>Talk to GitHub to read your code changes</p></li><li><p>Talk to AWS to read your real infrastructure</p></li><li><p>Talk to your AWS Bedrock Knowledge Bases to read your corporate data</p></li></ul><p>And also, to <strong>correlate changes among those platforms</strong> - that&#8217;s going to be a really powerful functionality as we move forward in maturing this technology. </p><p>I&#8217;m going to keep building super bots, and I&#8217;m very interested in AgentCore to establish memory and runtime observability for these bots. I&#8217;ll be digging into that next, keep an eye out! </p><p>Good luck out there. <br>kyler</p>]]></content:encoded></item><item><title><![CDATA[🔥Building a Teams Bot with AI Capabilities - Part 6 - Reading Teams Attachments🔥]]></title><description><![CDATA[aka, file location and permissions aren't reliable, and are critically important. Great.]]></description><link>https://www.letsdodevops.com/p/teamsai-6</link><guid isPermaLink="false">https://www.letsdodevops.com/p/teamsai-6</guid><dc:creator><![CDATA[Kyler Middleton]]></dc:creator><pubDate>Mon, 04 Aug 2025 02:01:48 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!uUCV!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f3cd9d6-d561-419d-855e-7d21b2cebe3a_1456x819.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote><p><em>This blog series focuses on presenting complex DevOps projects as simple and approachable via plain language and lots of pictures. You can do it!</em></p><p>These articles are supported by readers, <a href="https://www.letsdodevops.com/subscribe">please consider subscribing</a> to support me writing more of these articles &lt;3 :)</p><p>This article is part of a series of articles, because 1 article would be absolutely massive.</p></blockquote><ul><li><p>Part 1: <a href="https://www.letsdodevops.com/p/ai-teams-bot">Create an Azure Bot and App Registration</a></p></li><li><p>Part 2: <a href="https://www.letsdodevops.com/p/teamsai-2">Register Bot in Teams with Teams Developer Portal</a></p></li><li><p>Part 3: <a href="https://www.letsdodevops.com/p/teamsai-3">Delegated Permissions and Making Lambda Stateful for Oauth2</a></p></li><li><p>Part 4: <a href="https://www.letsdodevops.com/p/teamsai-4">Building the Receiver lambda to store tokens and state</a></p></li><li><p>Part 5: <a href="https://www.letsdodevops.com/p/teamsai-5">Finding messages, reading conversations in Teams</a></p></li><li><p>Part 6 (this article): Finding attachments and reading them into Bedrock</p></li></ul><p>Hey all!</p><p>In the last article we talked about how to find the root of messages to build our conversational context in Teams. That is an incredibly challenging task, as the Teams client is several raccoons in a trench coat (well, service APIs behind a front-end, same difference). Those services are generally: </p><ul><li><p><strong>Azure Entra/AD API</strong> - Authentication and Authorization (is Teams license assigned)</p></li><li><p><strong>OneDrive API</strong> - personal files. Files are shared to the person they are sent to automatically in Teams. </p></li></ul><blockquote><p>There is some weird behavior because of this. When building a multi-person chat, and sharing files, files are shared to each person in the chat <strong>at the time of sharing the file</strong>. If a person is added later, they can see previous messages (Teams API) but not previous files (OneDrive API), as files are not batch retroactively shared. #WeirdTeams</p></blockquote><ul><li><p><strong>SharePoint API</strong> - Team/Channel files. When files are uploaded to a Channel, they are shared with that Team automatically. </p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!uUCV!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f3cd9d6-d561-419d-855e-7d21b2cebe3a_1456x819.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!uUCV!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f3cd9d6-d561-419d-855e-7d21b2cebe3a_1456x819.jpeg 424w, https://substackcdn.com/image/fetch/$s_!uUCV!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f3cd9d6-d561-419d-855e-7d21b2cebe3a_1456x819.jpeg 848w, https://substackcdn.com/image/fetch/$s_!uUCV!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f3cd9d6-d561-419d-855e-7d21b2cebe3a_1456x819.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!uUCV!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f3cd9d6-d561-419d-855e-7d21b2cebe3a_1456x819.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!uUCV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f3cd9d6-d561-419d-855e-7d21b2cebe3a_1456x819.jpeg" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3f3cd9d6-d561-419d-855e-7d21b2cebe3a_1456x819.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:173764,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.letsdodevops.com/i/170045772?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2acbd0c0-dac7-45a9-a4a3-a24e87ee610a_1456x819.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!uUCV!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f3cd9d6-d561-419d-855e-7d21b2cebe3a_1456x819.jpeg 424w, https://substackcdn.com/image/fetch/$s_!uUCV!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f3cd9d6-d561-419d-855e-7d21b2cebe3a_1456x819.jpeg 848w, https://substackcdn.com/image/fetch/$s_!uUCV!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f3cd9d6-d561-419d-855e-7d21b2cebe3a_1456x819.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!uUCV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f3cd9d6-d561-419d-855e-7d21b2cebe3a_1456x819.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Teams stores files wherever it feels like, thank you. <a href="https://efficiency365.com/2020/01/12/where-to-store-files-with-office-365/">Image credit</a>. </figcaption></figure></div><p>To solve this, we have a robust couple functions that find, download, and encode properly the attachments that need to be added to the conversation context. </p><p>Lets walk through it. </p><h1>Iterate Through the Messages</h1><p>To start with, lets catch up with where the last article covered. We went through the functions that fetch the metadata and messages around &#8220;channel&#8221; type conversations (those in Teams/Channels), &#8220;personal&#8221; type messages (those in DMs), and how we &#8220;massage&#8221; the messages to scrub any junk that Teams returns to us in those API calls. There&#8217;s a lot there, it&#8217;s worth a read if you&#8217;re skipping ahead. </p><p>But all you really need to know now is that we have a list of all conversation messages in the thread. For DMs, we just respond to the single request (I&#8217;d love to add &#8220;threading&#8221;/&#8221;grouping of topics&#8221; in DM context in future, but haven&#8217;t found an elegant way yet), and for channels we read back all the messages to the root/parent message. </p><p>So that covers us up to line 11. </p><p>On line 11 and 12, we initialize a list of conversations and context, that we&#8217;ll populate in an iterative. </p><p>On line 15, we iterate through each message package that we&#8217;ve previously built to build conversation turns - the sort of atomic unit of a conversation that bedrock wants to read. We&#8217;ll walk through this more as we dig deeper into this function. </p><p>We take that turn (line 17) and append it to the conversation (line 19). </p><p>Once we have a complete conversation built in Bedrock&#8217;s style, we return it to the parent, which will do the Bedrock conversation. </p><p>For now, lets dig into how the build_conversation_turn() function works. </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist139904306\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-get_teams_conversation_history1-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;get_teams_conversation_history1.py content, created by KyMidd on 12:53AM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;8\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;get_teams_conversation_history1.py\&quot;>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history1-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history1-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-en>get_teams_conversation_history</span>(<span class=pl-s1>user_graph_auth_token</span>, <span class=pl-s1>event_body</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history1-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history1-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># ...</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history1-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history1-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>if</span> <span class=pl-s1>conversation_type</span> <span class=pl-c1>==</span> <span class=pl-s>&amp;quot;channel&amp;quot;</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history1-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history1-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># ...</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history1-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history1-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>elif</span> <span class=pl-s1>conversation_type</span> <span class=pl-c1>==</span> <span class=pl-s>&amp;quot;personal&amp;quot;</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history1-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history1-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># ...</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history1-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history1-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># This excludes the bot&amp;#39;s loading messages, the authentication card, and any messages which are invalid or blank</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history1-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history1-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>messages</span> <span class=pl-c1>=</span> <span class=pl-en>massage_messages</span>(<span class=pl-s1>messages</span>, <span class=pl-s1>conversation_type</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history1-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history1-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history1-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history1-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Initialize conversation and content lists</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history1-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history1-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>conversation</span> <span class=pl-c1>=</span> []</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history1-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history1-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>content</span> <span class=pl-c1>=</span> []</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history1-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history1-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history1-py-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history1-py-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Iterate through messages and build conversation</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history1-py-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history1-py-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>for</span> <span class=pl-s1>message</span> <span class=pl-c1>in</span> <span class=pl-s1>messages</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history1-py-L16\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;16\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history1-py-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># Iterate over content</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history1-py-L17\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;17\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history1-py-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>conversation_turn</span> <span class=pl-c1>=</span> <span class=pl-en>build_conversation_turn</span>(<span class=pl-s1>content</span>, <span class=pl-s1>message</span>, <span class=pl-s1>headers</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history1-py-L18\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;18\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history1-py-LC18\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># Append the conversation turn to the conversation</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history1-py-L19\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;19\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history1-py-LC19\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>conversation</span>.<span class=pl-c1>append</span>(<span class=pl-s1>conversation_turn</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history1-py-L20\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;20\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history1-py-LC20\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>return</span> <span class=pl-s1>conversation</span></td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/8516a3f08d1d013c30e97f9f15f80fb1/raw/8739392c151550ccab10a7dca80e02df514cd3d1/get_teams_conversation_history1.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/8516a3f08d1d013c30e97f9f15f80fb1#file-get_teams_conversation_history1-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          get_teams_conversation_history1.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-110350e56c50.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-110350e56c50.css"><div id="gist139904306" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-get_teams_conversation_history1-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="8" data-paste-markdown-skip="" data-tagsearch-path="get_teams_conversation_history1.py">
        <tbody><tr>
          <td id="file-get_teams_conversation_history1-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-get_teams_conversation_history1-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-en">get_teams_conversation_history</span>(<span class="pl-s1">user_graph_auth_token</span>, <span class="pl-s1">event_body</span>):</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history1-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-get_teams_conversation_history1-py-LC2" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># ...</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history1-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-get_teams_conversation_history1-py-LC3" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">if</span> <span class="pl-s1">conversation_type</span> <span class="pl-c1">==</span> <span class="pl-s">"channel"</span>:</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history1-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-get_teams_conversation_history1-py-LC4" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># ...</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history1-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-get_teams_conversation_history1-py-LC5" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">elif</span> <span class="pl-s1">conversation_type</span> <span class="pl-c1">==</span> <span class="pl-s">"personal"</span>:</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history1-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-get_teams_conversation_history1-py-LC6" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># ...</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history1-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-get_teams_conversation_history1-py-LC7" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># This excludes the bot's loading messages, the authentication card, and any messages which are invalid or blank</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history1-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-get_teams_conversation_history1-py-LC8" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">messages</span> <span class="pl-c1">=</span> <span class="pl-en">massage_messages</span>(<span class="pl-s1">messages</span>, <span class="pl-s1">conversation_type</span>)</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history1-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-get_teams_conversation_history1-py-LC9" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history1-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-get_teams_conversation_history1-py-LC10" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Initialize conversation and content lists</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history1-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-get_teams_conversation_history1-py-LC11" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">conversation</span> <span class="pl-c1">=</span> []</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history1-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-get_teams_conversation_history1-py-LC12" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">content</span> <span class="pl-c1">=</span> []</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history1-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-get_teams_conversation_history1-py-LC13" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history1-py-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-get_teams_conversation_history1-py-LC14" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Iterate through messages and build conversation</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history1-py-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-get_teams_conversation_history1-py-LC15" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">for</span> <span class="pl-s1">message</span> <span class="pl-c1">in</span> <span class="pl-s1">messages</span>:</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history1-py-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-get_teams_conversation_history1-py-LC16" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># Iterate over content</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history1-py-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-get_teams_conversation_history1-py-LC17" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">conversation_turn</span> <span class="pl-c1">=</span> <span class="pl-en">build_conversation_turn</span>(<span class="pl-s1">content</span>, <span class="pl-s1">message</span>, <span class="pl-s1">headers</span>)</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history1-py-L18" class="blob-num js-line-number js-blob-rnum" data-line-number="18"></td>
          <td id="file-get_teams_conversation_history1-py-LC18" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># Append the conversation turn to the conversation</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history1-py-L19" class="blob-num js-line-number js-blob-rnum" data-line-number="19"></td>
          <td id="file-get_teams_conversation_history1-py-LC19" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">conversation</span>.<span class="pl-c1">append</span>(<span class="pl-s1">conversation_turn</span>)</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history1-py-L20" class="blob-num js-line-number js-blob-rnum" data-line-number="20"></td>
          <td id="file-get_teams_conversation_history1-py-LC20" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">return</span> <span class="pl-s1">conversation</span></td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/8516a3f08d1d013c30e97f9f15f80fb1/raw/8739392c151550ccab10a7dca80e02df514cd3d1/get_teams_conversation_history1.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/8516a3f08d1d013c30e97f9f15f80fb1#file-get_teams_conversation_history1-py" class="Link--inTextBlock">
          get_teams_conversation_history1.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>Every single message from a user has from.user.displayName, and if it exists, we set the sender Role as &#8220;user&#8221;. Roles in Bedrock can be either &#8220;user&#8221; (not from the model) and &#8220;assistant&#8221; (from the model). </p><p>On line 7, if there is no displayName, we assume it&#8217;s the assistant. </p><p>On line 10, we get the message.body.content, which s the text of the message. Teams annoyingly uses html encoding, so we strip as much of that with simple regex as we can, line 13-14. I think Bedrock can interpret this fine, but it annoys the humans (read: me) when reading debug logs, so I remove it anyway. </p><p>On line 17 we start building the content block, making sure to include the sender name and text, something like: </p><blockquote><p>&#8220;Kyler Middleton says: Tell me the weather&#8221; </p></blockquote><p>And if there&#8217;s no sender_name (it&#8217;s the bot), we just include the text. We&#8217;ll make sure to set the role later on. </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist139904374\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-build_conversation_turn1-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;build_conversation_turn1.py content, created by KyMidd on 01:02AM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;8\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;build_conversation_turn1.py\&quot;>\n        <tr>\n          <td id=\&quot;file-build_conversation_turn1-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-build_conversation_turn1-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-en>build_conversation_turn</span>(<span class=pl-s1>content</span>, <span class=pl-s1>message</span>, <span class=pl-s1>headers</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_conversation_turn1-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-build_conversation_turn1-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Get sender name</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_conversation_turn1-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-build_conversation_turn1-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>try</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_conversation_turn1-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-build_conversation_turn1-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>sender_name</span> <span class=pl-c1>=</span> <span class=pl-s1>message</span>[<span class=pl-s>&amp;quot;from&amp;quot;</span>][<span class=pl-s>&amp;quot;user&amp;quot;</span>][<span class=pl-s>&amp;quot;displayName&amp;quot;</span>]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_conversation_turn1-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-build_conversation_turn1-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>sender_role</span> <span class=pl-c1>=</span> <span class=pl-s>&amp;quot;user&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_conversation_turn1-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-build_conversation_turn1-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>except</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_conversation_turn1-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-build_conversation_turn1-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>sender_role</span> <span class=pl-c1>=</span> <span class=pl-s>&amp;quot;assistant&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_conversation_turn1-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-build_conversation_turn1-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_conversation_turn1-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-build_conversation_turn1-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Get text</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_conversation_turn1-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-build_conversation_turn1-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>content_text</span> <span class=pl-c1>=</span> <span class=pl-s1>message</span>.<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;body&amp;quot;</span>, {}).<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;content&amp;quot;</span>, <span class=pl-s>&amp;quot;&amp;quot;</span>).<span class=pl-c1>strip</span>()</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_conversation_turn1-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-build_conversation_turn1-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_conversation_turn1-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-build_conversation_turn1-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Clean text, Teams adds a lot of extra html like this: &amp;quot;text&amp;quot;: &amp;quot;Kyler Middleton says: &amp;lt;p&amp;gt;Hey Vera, what&amp;#39;s up today?&amp;amp;nbsp;&amp;lt;/p&amp;gt;&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_conversation_turn1-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-build_conversation_turn1-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>content_text</span> <span class=pl-c1>=</span> <span class=pl-s1>re</span>.<span class=pl-c1>sub</span>(<span class=pl-s>r&amp;quot;&amp;lt;[^&amp;gt;]+&amp;gt;&amp;quot;</span>, <span class=pl-s>&amp;quot;&amp;quot;</span>, <span class=pl-s1>content_text</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_conversation_turn1-py-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-build_conversation_turn1-py-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>content_text</span> <span class=pl-c1>=</span> <span class=pl-s1>content_text</span>.<span class=pl-c1>replace</span>(<span class=pl-s>&amp;quot;&amp;amp;nbsp;&amp;quot;</span>, <span class=pl-s>&amp;quot; &amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_conversation_turn1-py-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-build_conversation_turn1-py-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_conversation_turn1-py-L16\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;16\&quot;></td>\n          <td id=\&quot;file-build_conversation_turn1-py-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Set the content block</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_conversation_turn1-py-L17\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;17\&quot;></td>\n          <td id=\&quot;file-build_conversation_turn1-py-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>if</span> <span class=pl-s1>sender_role</span> <span class=pl-c1>==</span> <span class=pl-s>&amp;quot;user&amp;quot;</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_conversation_turn1-py-L18\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;18\&quot;></td>\n          <td id=\&quot;file-build_conversation_turn1-py-LC18\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>content</span> <span class=pl-c1>=</span> [</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_conversation_turn1-py-L19\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;19\&quot;></td>\n          <td id=\&quot;file-build_conversation_turn1-py-LC19\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_conversation_turn1-py-L20\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;20\&quot;></td>\n          <td id=\&quot;file-build_conversation_turn1-py-LC20\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                <span class=pl-s>&amp;quot;text&amp;quot;</span>: <span class=pl-s>f&amp;quot;<span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>sender_name</span><span class=pl-kos>}</span></span> says: <span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>content_text</span><span class=pl-kos>}</span></span>&amp;quot;</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_conversation_turn1-py-L21\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;21\&quot;></td>\n          <td id=\&quot;file-build_conversation_turn1-py-LC21\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            }</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_conversation_turn1-py-L22\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;22\&quot;></td>\n          <td id=\&quot;file-build_conversation_turn1-py-LC22\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        ]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_conversation_turn1-py-L23\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;23\&quot;></td>\n          <td id=\&quot;file-build_conversation_turn1-py-LC23\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>else</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_conversation_turn1-py-L24\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;24\&quot;></td>\n          <td id=\&quot;file-build_conversation_turn1-py-LC24\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>content</span> <span class=pl-c1>=</span> [</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_conversation_turn1-py-L25\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;25\&quot;></td>\n          <td id=\&quot;file-build_conversation_turn1-py-LC25\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_conversation_turn1-py-L26\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;26\&quot;></td>\n          <td id=\&quot;file-build_conversation_turn1-py-LC26\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                <span class=pl-s>&amp;quot;text&amp;quot;</span>: <span class=pl-s>f&amp;quot;<span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>content_text</span><span class=pl-kos>}</span></span>&amp;quot;</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_conversation_turn1-py-L27\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;27\&quot;></td>\n          <td id=\&quot;file-build_conversation_turn1-py-LC27\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            }</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_conversation_turn1-py-L28\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;28\&quot;></td>\n          <td id=\&quot;file-build_conversation_turn1-py-LC28\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        ]</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/0a04bb892666ea060d9eb16e7c2e5cdc/raw/8debfa7effcea2867a8c45ebd971ba93597e0e65/build_conversation_turn1.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/0a04bb892666ea060d9eb16e7c2e5cdc#file-build_conversation_turn1-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          build_conversation_turn1.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-110350e56c50.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-110350e56c50.css"><div id="gist139904374" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-build_conversation_turn1-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="8" data-paste-markdown-skip="" data-tagsearch-path="build_conversation_turn1.py">
        <tbody><tr>
          <td id="file-build_conversation_turn1-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-build_conversation_turn1-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-en">build_conversation_turn</span>(<span class="pl-s1">content</span>, <span class="pl-s1">message</span>, <span class="pl-s1">headers</span>):</td>
        </tr>
        <tr>
          <td id="file-build_conversation_turn1-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-build_conversation_turn1-py-LC2" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Get sender name</span></td>
        </tr>
        <tr>
          <td id="file-build_conversation_turn1-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-build_conversation_turn1-py-LC3" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">try</span>:</td>
        </tr>
        <tr>
          <td id="file-build_conversation_turn1-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-build_conversation_turn1-py-LC4" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">sender_name</span> <span class="pl-c1">=</span> <span class="pl-s1">message</span>[<span class="pl-s">"from"</span>][<span class="pl-s">"user"</span>][<span class="pl-s">"displayName"</span>]</td>
        </tr>
        <tr>
          <td id="file-build_conversation_turn1-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-build_conversation_turn1-py-LC5" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">sender_role</span> <span class="pl-c1">=</span> <span class="pl-s">"user"</span></td>
        </tr>
        <tr>
          <td id="file-build_conversation_turn1-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-build_conversation_turn1-py-LC6" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">except</span>:</td>
        </tr>
        <tr>
          <td id="file-build_conversation_turn1-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-build_conversation_turn1-py-LC7" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">sender_role</span> <span class="pl-c1">=</span> <span class="pl-s">"assistant"</span></td>
        </tr>
        <tr>
          <td id="file-build_conversation_turn1-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-build_conversation_turn1-py-LC8" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-build_conversation_turn1-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-build_conversation_turn1-py-LC9" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Get text</span></td>
        </tr>
        <tr>
          <td id="file-build_conversation_turn1-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-build_conversation_turn1-py-LC10" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">content_text</span> <span class="pl-c1">=</span> <span class="pl-s1">message</span>.<span class="pl-c1">get</span>(<span class="pl-s">"body"</span>, {}).<span class="pl-c1">get</span>(<span class="pl-s">"content"</span>, <span class="pl-s">""</span>).<span class="pl-c1">strip</span>()</td>
        </tr>
        <tr>
          <td id="file-build_conversation_turn1-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-build_conversation_turn1-py-LC11" class="blob-code blob-code-inner js-file-line">    </td>
        </tr>
        <tr>
          <td id="file-build_conversation_turn1-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-build_conversation_turn1-py-LC12" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Clean text, Teams adds a lot of extra html like this: "text": "Kyler Middleton says: &lt;p&gt;Hey Vera, what's up today?&amp;nbsp;&lt;/p&gt;"</span></td>
        </tr>
        <tr>
          <td id="file-build_conversation_turn1-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-build_conversation_turn1-py-LC13" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">content_text</span> <span class="pl-c1">=</span> <span class="pl-s1">re</span>.<span class="pl-c1">sub</span>(<span class="pl-s">r"&lt;[^&gt;]+&gt;"</span>, <span class="pl-s">""</span>, <span class="pl-s1">content_text</span>)</td>
        </tr>
        <tr>
          <td id="file-build_conversation_turn1-py-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-build_conversation_turn1-py-LC14" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">content_text</span> <span class="pl-c1">=</span> <span class="pl-s1">content_text</span>.<span class="pl-c1">replace</span>(<span class="pl-s">"&amp;nbsp;"</span>, <span class="pl-s">" "</span>)</td>
        </tr>
        <tr>
          <td id="file-build_conversation_turn1-py-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-build_conversation_turn1-py-LC15" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-build_conversation_turn1-py-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-build_conversation_turn1-py-LC16" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Set the content block</span></td>
        </tr>
        <tr>
          <td id="file-build_conversation_turn1-py-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-build_conversation_turn1-py-LC17" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">if</span> <span class="pl-s1">sender_role</span> <span class="pl-c1">==</span> <span class="pl-s">"user"</span>:</td>
        </tr>
        <tr>
          <td id="file-build_conversation_turn1-py-L18" class="blob-num js-line-number js-blob-rnum" data-line-number="18"></td>
          <td id="file-build_conversation_turn1-py-LC18" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">content</span> <span class="pl-c1">=</span> [</td>
        </tr>
        <tr>
          <td id="file-build_conversation_turn1-py-L19" class="blob-num js-line-number js-blob-rnum" data-line-number="19"></td>
          <td id="file-build_conversation_turn1-py-LC19" class="blob-code blob-code-inner js-file-line">            {</td>
        </tr>
        <tr>
          <td id="file-build_conversation_turn1-py-L20" class="blob-num js-line-number js-blob-rnum" data-line-number="20"></td>
          <td id="file-build_conversation_turn1-py-LC20" class="blob-code blob-code-inner js-file-line">                <span class="pl-s">"text"</span>: <span class="pl-s">f"<span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">sender_name</span><span class="pl-kos">}</span></span> says: <span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">content_text</span><span class="pl-kos">}</span></span>"</span>,</td>
        </tr>
        <tr>
          <td id="file-build_conversation_turn1-py-L21" class="blob-num js-line-number js-blob-rnum" data-line-number="21"></td>
          <td id="file-build_conversation_turn1-py-LC21" class="blob-code blob-code-inner js-file-line">            }</td>
        </tr>
        <tr>
          <td id="file-build_conversation_turn1-py-L22" class="blob-num js-line-number js-blob-rnum" data-line-number="22"></td>
          <td id="file-build_conversation_turn1-py-LC22" class="blob-code blob-code-inner js-file-line">        ]</td>
        </tr>
        <tr>
          <td id="file-build_conversation_turn1-py-L23" class="blob-num js-line-number js-blob-rnum" data-line-number="23"></td>
          <td id="file-build_conversation_turn1-py-LC23" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">else</span>:</td>
        </tr>
        <tr>
          <td id="file-build_conversation_turn1-py-L24" class="blob-num js-line-number js-blob-rnum" data-line-number="24"></td>
          <td id="file-build_conversation_turn1-py-LC24" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">content</span> <span class="pl-c1">=</span> [</td>
        </tr>
        <tr>
          <td id="file-build_conversation_turn1-py-L25" class="blob-num js-line-number js-blob-rnum" data-line-number="25"></td>
          <td id="file-build_conversation_turn1-py-LC25" class="blob-code blob-code-inner js-file-line">            {</td>
        </tr>
        <tr>
          <td id="file-build_conversation_turn1-py-L26" class="blob-num js-line-number js-blob-rnum" data-line-number="26"></td>
          <td id="file-build_conversation_turn1-py-LC26" class="blob-code blob-code-inner js-file-line">                <span class="pl-s">"text"</span>: <span class="pl-s">f"<span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">content_text</span><span class="pl-kos">}</span></span>"</span>,</td>
        </tr>
        <tr>
          <td id="file-build_conversation_turn1-py-L27" class="blob-num js-line-number js-blob-rnum" data-line-number="27"></td>
          <td id="file-build_conversation_turn1-py-LC27" class="blob-code blob-code-inner js-file-line">            }</td>
        </tr>
        <tr>
          <td id="file-build_conversation_turn1-py-L28" class="blob-num js-line-number js-blob-rnum" data-line-number="28"></td>
          <td id="file-build_conversation_turn1-py-LC28" class="blob-code blob-code-inner js-file-line">        ]</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/0a04bb892666ea060d9eb16e7c2e5cdc/raw/8debfa7effcea2867a8c45ebd971ba93597e0e65/build_conversation_turn1.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/0a04bb892666ea060d9eb16e7c2e5cdc#file-build_conversation_turn1-py" class="Link--inTextBlock">
          build_conversation_turn1.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><h1>Downloading the Attachments</h1><p>Next we check for any attachments in the message. This isn&#8217;t a straight-forward process (obviously, it gets it own article in this series). </p><p>The attachment always has a file name (file.name) and file_extension (everything after the last period in the file name), line 8. </p><p>We check for an attribute called the file.contentUrl which is the URL address of the content. There&#8217;s different ways to authenticate and build the URL to get the file (I miss the simplicity of slack attachments dearly here), so we pass that url to the download_file_for_users() function, line 14. We&#8217;ll dig into that shortly. </p><p>There&#8217;s a couple of other gotchas here we&#8217;ll address before we dive into downloading. Bedrock has pretty strict requirements on document names, so we file the name through regex, line 18. We&#8217;re only permitted single spaces (Weird?) so we regex that as well, line 20. We also don&#8217;t permit leading or trailing spaces, so we remove those on line 22. </p><blockquote><p>Many of these text manipulations could be combined into single lines, but I abhor complex code. DRY is awful, I&#8217;d much rather understand exactly what code is doing. </p></blockquote><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist139904431\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-build_conversation_turn2-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;build_conversation_turn2.py content, created by KyMidd on 01:08AM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;8\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;build_conversation_turn2.py\&quot;>\n        <tr>\n          <td id=\&quot;file-build_conversation_turn2-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-build_conversation_turn2-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-en>build_conversation_turn</span>(<span class=pl-s1>content</span>, <span class=pl-s1>message</span>, <span class=pl-s1>headers</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_conversation_turn2-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-build_conversation_turn2-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c>#...</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_conversation_turn2-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-build_conversation_turn2-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Iterate over attachments</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_conversation_turn2-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-build_conversation_turn2-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>if</span> <span class=pl-s>&amp;quot;attachments&amp;quot;</span> <span class=pl-c1>in</span> <span class=pl-s1>message</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_conversation_turn2-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-build_conversation_turn2-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>for</span> <span class=pl-s1>file</span> <span class=pl-c1>in</span> <span class=pl-s1>message</span>[<span class=pl-s>&amp;quot;attachments&amp;quot;</span>]:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_conversation_turn2-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-build_conversation_turn2-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>          <span class=pl-c># Isolate name of the file and remove characters before the final period</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_conversation_turn2-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-build_conversation_turn2-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>file_name</span> <span class=pl-c1>=</span> <span class=pl-s1>file</span>[<span class=pl-s>&amp;quot;name&amp;quot;</span>] </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_conversation_turn2-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-build_conversation_turn2-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>file_extension</span> <span class=pl-c1>=</span> <span class=pl-s1>file</span>[<span class=pl-s>&amp;quot;name&amp;quot;</span>].<span class=pl-c1>split</span>(<span class=pl-s>&amp;quot;.&amp;quot;</span>)[<span class=pl-c1>-</span><span class=pl-c1>1</span>]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_conversation_turn2-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-build_conversation_turn2-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_conversation_turn2-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-build_conversation_turn2-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-c># File is a supported type</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_conversation_turn2-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-build_conversation_turn2-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>file_url</span> <span class=pl-c1>=</span> <span class=pl-s1>file</span>[<span class=pl-s>&amp;quot;contentUrl&amp;quot;</span>]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_conversation_turn2-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-build_conversation_turn2-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_conversation_turn2-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-build_conversation_turn2-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-c># Download the file from the content URL. Include headers in case the file is protected (SharePoint)</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_conversation_turn2-py-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-build_conversation_turn2-py-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>file_content</span> <span class=pl-c1>=</span> <span class=pl-en>download_file_for_user</span>(<span class=pl-s1>file_name</span>, <span class=pl-s1>file_url</span>, <span class=pl-s1>headers</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_conversation_turn2-py-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-build_conversation_turn2-py-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_conversation_turn2-py-L16\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;16\&quot;></td>\n          <td id=\&quot;file-build_conversation_turn2-py-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-c>### Bedrock API has strict file name reqs: document file name can only contain alphanumeric characters, whitespace characters, hyphens, parentheses, and square brackets. The name can&amp;#39;t contain more than one consecutive whitespace character.</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_conversation_turn2-py-L17\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;17\&quot;></td>\n          <td id=\&quot;file-build_conversation_turn2-py-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-c># Remove disallowed</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_conversation_turn2-py-L18\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;18\&quot;></td>\n          <td id=\&quot;file-build_conversation_turn2-py-LC18\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>file_name</span> <span class=pl-c1>=</span> <span class=pl-s1>re</span>.<span class=pl-c1>sub</span>(<span class=pl-s>r&amp;quot;[^a-zA-Z0-9\\s\\-\\[\\]\\(\\)]&amp;quot;</span>, <span class=pl-s>&amp;quot;&amp;quot;</span>, <span class=pl-s1>file_name</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_conversation_turn2-py-L19\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;19\&quot;></td>\n          <td id=\&quot;file-build_conversation_turn2-py-LC19\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-c># Only single spaces allowed</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_conversation_turn2-py-L20\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;20\&quot;></td>\n          <td id=\&quot;file-build_conversation_turn2-py-LC20\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>file_name</span> <span class=pl-c1>=</span> <span class=pl-s1>re</span>.<span class=pl-c1>sub</span>(<span class=pl-s>r&amp;quot;\\s{2,}&amp;quot;</span>, <span class=pl-s>&amp;quot; &amp;quot;</span>, <span class=pl-s1>file_name</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_conversation_turn2-py-L21\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;21\&quot;></td>\n          <td id=\&quot;file-build_conversation_turn2-py-LC21\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-c># Strip leading and trailing whitespace</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_conversation_turn2-py-L22\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;22\&quot;></td>\n          <td id=\&quot;file-build_conversation_turn2-py-LC22\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>file_name</span> <span class=pl-c1>=</span> <span class=pl-s1>file_name</span>.<span class=pl-c1>strip</span>()</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/2f9987ddeacbc721cce2ce542835fd2a/raw/b372059ef817e39e9beb5c802b320ea0f9875524/build_conversation_turn2.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/2f9987ddeacbc721cce2ce542835fd2a#file-build_conversation_turn2-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          build_conversation_turn2.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-110350e56c50.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-110350e56c50.css"><div id="gist139904431" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-build_conversation_turn2-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="8" data-paste-markdown-skip="" data-tagsearch-path="build_conversation_turn2.py">
        <tbody><tr>
          <td id="file-build_conversation_turn2-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-build_conversation_turn2-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-en">build_conversation_turn</span>(<span class="pl-s1">content</span>, <span class="pl-s1">message</span>, <span class="pl-s1">headers</span>):</td>
        </tr>
        <tr>
          <td id="file-build_conversation_turn2-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-build_conversation_turn2-py-LC2" class="blob-code blob-code-inner js-file-line">    <span class="pl-c">#...</span></td>
        </tr>
        <tr>
          <td id="file-build_conversation_turn2-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-build_conversation_turn2-py-LC3" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Iterate over attachments</span></td>
        </tr>
        <tr>
          <td id="file-build_conversation_turn2-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-build_conversation_turn2-py-LC4" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">if</span> <span class="pl-s">"attachments"</span> <span class="pl-c1">in</span> <span class="pl-s1">message</span>:</td>
        </tr>
        <tr>
          <td id="file-build_conversation_turn2-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-build_conversation_turn2-py-LC5" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">for</span> <span class="pl-s1">file</span> <span class="pl-c1">in</span> <span class="pl-s1">message</span>[<span class="pl-s">"attachments"</span>]:</td>
        </tr>
        <tr>
          <td id="file-build_conversation_turn2-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-build_conversation_turn2-py-LC6" class="blob-code blob-code-inner js-file-line">          <span class="pl-c"># Isolate name of the file and remove characters before the final period</span></td>
        </tr>
        <tr>
          <td id="file-build_conversation_turn2-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-build_conversation_turn2-py-LC7" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">file_name</span> <span class="pl-c1">=</span> <span class="pl-s1">file</span>[<span class="pl-s">"name"</span>] </td>
        </tr>
        <tr>
          <td id="file-build_conversation_turn2-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-build_conversation_turn2-py-LC8" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">file_extension</span> <span class="pl-c1">=</span> <span class="pl-s1">file</span>[<span class="pl-s">"name"</span>].<span class="pl-c1">split</span>(<span class="pl-s">"."</span>)[<span class="pl-c1">-</span><span class="pl-c1">1</span>]</td>
        </tr>
        <tr>
          <td id="file-build_conversation_turn2-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-build_conversation_turn2-py-LC9" class="blob-code blob-code-inner js-file-line">            </td>
        </tr>
        <tr>
          <td id="file-build_conversation_turn2-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-build_conversation_turn2-py-LC10" class="blob-code blob-code-inner js-file-line">            <span class="pl-c"># File is a supported type</span></td>
        </tr>
        <tr>
          <td id="file-build_conversation_turn2-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-build_conversation_turn2-py-LC11" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">file_url</span> <span class="pl-c1">=</span> <span class="pl-s1">file</span>[<span class="pl-s">"contentUrl"</span>]</td>
        </tr>
        <tr>
          <td id="file-build_conversation_turn2-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-build_conversation_turn2-py-LC12" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-build_conversation_turn2-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-build_conversation_turn2-py-LC13" class="blob-code blob-code-inner js-file-line">            <span class="pl-c"># Download the file from the content URL. Include headers in case the file is protected (SharePoint)</span></td>
        </tr>
        <tr>
          <td id="file-build_conversation_turn2-py-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-build_conversation_turn2-py-LC14" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">file_content</span> <span class="pl-c1">=</span> <span class="pl-en">download_file_for_user</span>(<span class="pl-s1">file_name</span>, <span class="pl-s1">file_url</span>, <span class="pl-s1">headers</span>)</td>
        </tr>
        <tr>
          <td id="file-build_conversation_turn2-py-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-build_conversation_turn2-py-LC15" class="blob-code blob-code-inner js-file-line">            </td>
        </tr>
        <tr>
          <td id="file-build_conversation_turn2-py-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-build_conversation_turn2-py-LC16" class="blob-code blob-code-inner js-file-line">            <span class="pl-c">### Bedrock API has strict file name reqs: document file name can only contain alphanumeric characters, whitespace characters, hyphens, parentheses, and square brackets. The name can't contain more than one consecutive whitespace character.</span></td>
        </tr>
        <tr>
          <td id="file-build_conversation_turn2-py-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-build_conversation_turn2-py-LC17" class="blob-code blob-code-inner js-file-line">            <span class="pl-c"># Remove disallowed</span></td>
        </tr>
        <tr>
          <td id="file-build_conversation_turn2-py-L18" class="blob-num js-line-number js-blob-rnum" data-line-number="18"></td>
          <td id="file-build_conversation_turn2-py-LC18" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">file_name</span> <span class="pl-c1">=</span> <span class="pl-s1">re</span>.<span class="pl-c1">sub</span>(<span class="pl-s">r"[^a-zA-Z0-9\s\-\[\]\(\)]"</span>, <span class="pl-s">""</span>, <span class="pl-s1">file_name</span>)</td>
        </tr>
        <tr>
          <td id="file-build_conversation_turn2-py-L19" class="blob-num js-line-number js-blob-rnum" data-line-number="19"></td>
          <td id="file-build_conversation_turn2-py-LC19" class="blob-code blob-code-inner js-file-line">            <span class="pl-c"># Only single spaces allowed</span></td>
        </tr>
        <tr>
          <td id="file-build_conversation_turn2-py-L20" class="blob-num js-line-number js-blob-rnum" data-line-number="20"></td>
          <td id="file-build_conversation_turn2-py-LC20" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">file_name</span> <span class="pl-c1">=</span> <span class="pl-s1">re</span>.<span class="pl-c1">sub</span>(<span class="pl-s">r"\s{2,}"</span>, <span class="pl-s">" "</span>, <span class="pl-s1">file_name</span>)</td>
        </tr>
        <tr>
          <td id="file-build_conversation_turn2-py-L21" class="blob-num js-line-number js-blob-rnum" data-line-number="21"></td>
          <td id="file-build_conversation_turn2-py-LC21" class="blob-code blob-code-inner js-file-line">            <span class="pl-c"># Strip leading and trailing whitespace</span></td>
        </tr>
        <tr>
          <td id="file-build_conversation_turn2-py-L22" class="blob-num js-line-number js-blob-rnum" data-line-number="22"></td>
          <td id="file-build_conversation_turn2-py-LC22" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">file_name</span> <span class="pl-c1">=</span> <span class="pl-s1">file_name</span>.<span class="pl-c1">strip</span>()</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/2f9987ddeacbc721cce2ce542835fd2a/raw/b372059ef817e39e9beb5c802b320ea0f9875524/build_conversation_turn2.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/2f9987ddeacbc721cce2ce542835fd2a#file-build_conversation_turn2-py" class="Link--inTextBlock">
          build_conversation_turn2.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><h1>Pain</h1><p>It&#8217;s worth including the note for this function. All my functions have notes that describe broadly what they do, but this one is largely an apology on behalf of Teams&#8217; APIs. I don&#8217;t know why the Teams&#8230; Team decided to make all of this such a pain to work with, but I hope they stub their toes walking to their beds after turning out the lights. </p><blockquote><p>This function is more than 100 lines of code, so buckle up &#128557;</p></blockquote><p>First, on line 9, we wrap all this in a try. Why? Because we&#8217;re doing an extraordinary amount of logic here, and if we fail any of it we want our script to continue working. If we can&#8217;t fetch the attachment, we just continue with the text only. That&#8217;s just fine. </p><p>First, we check to see if the file_url contains sharepoint.com/sites/, which means the file is stored in SharePoint. We need to fetch the file from SharePoint, which turns out to be an incredibly annoying thing to do. </p><p>First, we need the site_id. Lets build a graph call to go fetch that. We establish the primitives by splitting the URL, line 13-17, and then build the URL, line 20. We require the file, line 21, via requests, and check the call, line 22. If it worked, we set the site_id to the response.id, line 23. </p><p>With that in hand, we need the default document library ID, so we build a URL to fetch THAT, line 26-29. </p><p>Then we need to extract the file path. These sometimes work just fine, and sometimes contain characters (why the heck?). We use a URL encoding function to do the heavy lifting of encoding the full URL plus document name (spaces and characters and such get encoded a bit weird in http), line 32. </p><p>Then we FINALLY finally try to download the file, line 34. </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist139904496\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-download_file_for_user1-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;download_file_for_user1.py content, created by KyMidd on 01:16AM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;8\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;download_file_for_user1.py\&quot;>\n        <tr>\n          <td id=\&quot;file-download_file_for_user1-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-download_file_for_user1-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c># Download file for user to bot</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user1-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-download_file_for_user1-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c># I apologize to future developers who have to maintain this code. </span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user1-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-download_file_for_user1-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c># I can only blame the byzantine nature of the Microsoft Graph API and Teams API, </span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user1-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-download_file_for_user1-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c># which stores files in whatever location it feels like, and then </span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user1-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-download_file_for_user1-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c># provides a content URL that may or may not work.</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user1-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-download_file_for_user1-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-s1>download_file_for_user</span>(<span class=pl-s1>file_name</span>, <span class=pl-s1>file_url</span>, <span class=pl-s1>headers</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user1-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-download_file_for_user1-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># If file is shared in a Team channel, the file is stored in sharepoint for the Team</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user1-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-download_file_for_user1-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># We need to decode and fetch a great deal of info about sharepoint to fetch the file</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user1-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-download_file_for_user1-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>try</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user1-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-download_file_for_user1-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>if</span> <span class=pl-s>&amp;quot;.sharepoint.com/sites/&amp;quot;</span> <span class=pl-c1>in</span> <span class=pl-s1>file_url</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user1-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-download_file_for_user1-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user1-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-download_file_for_user1-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-c># Parse SharePoint URL</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user1-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-download_file_for_user1-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>parts</span> <span class=pl-c1>=</span> <span class=pl-s1>file_url</span>.<span class=pl-c1>split</span>(<span class=pl-s>&amp;quot;.sharepoint.com/sites/&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user1-py-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-download_file_for_user1-py-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>hostname</span> <span class=pl-c1>=</span> <span class=pl-s1>parts</span>[<span class=pl-c1>0</span>].<span class=pl-c1>replace</span>(<span class=pl-s>&amp;quot;https://&amp;quot;</span>, <span class=pl-s>&amp;quot;&amp;quot;</span>) <span class=pl-c1>+</span> <span class=pl-s>&amp;quot;.sharepoint.com&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user1-py-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-download_file_for_user1-py-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>site_path_and_file</span> <span class=pl-c1>=</span> <span class=pl-s1>parts</span>[<span class=pl-c1>1</span>]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user1-py-L16\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;16\&quot;></td>\n          <td id=\&quot;file-download_file_for_user1-py-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>site_name</span> <span class=pl-c1>=</span> <span class=pl-s1>site_path_and_file</span>.<span class=pl-c1>split</span>(<span class=pl-s>&amp;quot;/&amp;quot;</span>)[<span class=pl-c1>0</span>]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user1-py-L17\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;17\&quot;></td>\n          <td id=\&quot;file-download_file_for_user1-py-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>file_relative_path_raw</span> <span class=pl-c1>=</span> <span class=pl-s1>site_path_and_file</span>[<span class=pl-en>len</span>(<span class=pl-s1>site_name</span>)<span class=pl-c1>+</span><span class=pl-c1>1</span>:]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user1-py-L18\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;18\&quot;></td>\n          <td id=\&quot;file-download_file_for_user1-py-LC18\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user1-py-L19\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;19\&quot;></td>\n          <td id=\&quot;file-download_file_for_user1-py-LC19\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-c># Get the SharePoint site ID</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user1-py-L20\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;20\&quot;></td>\n          <td id=\&quot;file-download_file_for_user1-py-LC20\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>site_lookup_url</span> <span class=pl-c1>=</span> <span class=pl-s>f&amp;quot;https://graph.microsoft.com/v1.0/sites/<span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>hostname</span><span class=pl-kos>}</span></span>:/sites/<span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>site_name</span><span class=pl-kos>}</span></span>&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user1-py-L21\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;21\&quot;></td>\n          <td id=\&quot;file-download_file_for_user1-py-LC21\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>site_resp</span> <span class=pl-c1>=</span> <span class=pl-s1>requests</span>.<span class=pl-c1>get</span>(<span class=pl-s1>site_lookup_url</span>, <span class=pl-s1>headers</span><span class=pl-c1>=</span><span class=pl-s1>headers</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user1-py-L22\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;22\&quot;></td>\n          <td id=\&quot;file-download_file_for_user1-py-LC22\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>site_resp</span>.<span class=pl-c1>raise_for_status</span>()</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user1-py-L23\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;23\&quot;></td>\n          <td id=\&quot;file-download_file_for_user1-py-LC23\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>site_id</span> <span class=pl-c1>=</span> <span class=pl-s1>site_resp</span>.<span class=pl-c1>json</span>()[<span class=pl-s>&amp;quot;id&amp;quot;</span>]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user1-py-L24\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;24\&quot;></td>\n          <td id=\&quot;file-download_file_for_user1-py-LC24\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user1-py-L25\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;25\&quot;></td>\n          <td id=\&quot;file-download_file_for_user1-py-LC25\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-c># Get default document library (drive) ID</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user1-py-L26\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;26\&quot;></td>\n          <td id=\&quot;file-download_file_for_user1-py-LC26\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>drive_url</span> <span class=pl-c1>=</span> <span class=pl-s>f&amp;quot;https://graph.microsoft.com/v1.0/sites/<span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>site_id</span><span class=pl-kos>}</span></span>/drive&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user1-py-L27\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;27\&quot;></td>\n          <td id=\&quot;file-download_file_for_user1-py-LC27\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>drive_resp</span> <span class=pl-c1>=</span> <span class=pl-s1>requests</span>.<span class=pl-c1>get</span>(<span class=pl-s1>drive_url</span>, <span class=pl-s1>headers</span><span class=pl-c1>=</span><span class=pl-s1>headers</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user1-py-L28\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;28\&quot;></td>\n          <td id=\&quot;file-download_file_for_user1-py-LC28\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>drive_resp</span>.<span class=pl-c1>raise_for_status</span>()</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user1-py-L29\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;29\&quot;></td>\n          <td id=\&quot;file-download_file_for_user1-py-LC29\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>drive_id</span> <span class=pl-c1>=</span> <span class=pl-s1>drive_resp</span>.<span class=pl-c1>json</span>()[<span class=pl-s>&amp;quot;id&amp;quot;</span>]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user1-py-L30\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;30\&quot;></td>\n          <td id=\&quot;file-download_file_for_user1-py-LC30\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user1-py-L31\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;31\&quot;></td>\n          <td id=\&quot;file-download_file_for_user1-py-LC31\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-c># Try the exact file path, there are sometimes invalid unicode characters like \\u202f embedded</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user1-py-L32\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;32\&quot;></td>\n          <td id=\&quot;file-download_file_for_user1-py-LC32\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>file_path_encoded</span> <span class=pl-c1>=</span> <span class=pl-s1>urllib</span>.<span class=pl-c1>parse</span>.<span class=pl-c1>quote</span>(<span class=pl-s1>file_relative_path_raw</span>, <span class=pl-s1>safe</span><span class=pl-c1>=</span><span class=pl-s>&amp;quot;/&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user1-py-L33\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;33\&quot;></td>\n          <td id=\&quot;file-download_file_for_user1-py-LC33\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>graph_url</span> <span class=pl-c1>=</span> <span class=pl-s>f&amp;quot;https://graph.microsoft.com/v1.0/sites/<span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>site_id</span><span class=pl-kos>}</span></span>/drives/<span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>drive_id</span><span class=pl-kos>}</span></span>/root:/<span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>file_path_encoded</span><span class=pl-kos>}</span></span>:/content&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user1-py-L34\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;34\&quot;></td>\n          <td id=\&quot;file-download_file_for_user1-py-LC34\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>resp</span> <span class=pl-c1>=</span> <span class=pl-s1>requests</span>.<span class=pl-c1>get</span>(<span class=pl-s1>graph_url</span>, <span class=pl-s1>headers</span><span class=pl-c1>=</span><span class=pl-s1>headers</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user1-py-L35\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;35\&quot;></td>\n          <td id=\&quot;file-download_file_for_user1-py-LC35\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-k>if</span> <span class=pl-s1>resp</span>.<span class=pl-c1>status_code</span> <span class=pl-c1>==</span> <span class=pl-c1>200</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user1-py-L36\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;36\&quot;></td>\n          <td id=\&quot;file-download_file_for_user1-py-LC36\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                <span class=pl-en>print</span>(<span class=pl-s>f&amp;quot;&#128994; Download succeeded from SharePoint site using exact path: <span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>graph_url</span><span class=pl-kos>}</span></span>&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user1-py-L37\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;37\&quot;></td>\n          <td id=\&quot;file-download_file_for_user1-py-LC37\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                <span class=pl-k>return</span> <span class=pl-s1>resp</span>.<span class=pl-c1>content</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user1-py-L38\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;38\&quot;></td>\n          <td id=\&quot;file-download_file_for_user1-py-LC38\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-k>else</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user1-py-L39\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;39\&quot;></td>\n          <td id=\&quot;file-download_file_for_user1-py-LC39\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                <span class=pl-en>print</span>(<span class=pl-s>f&amp;quot;&#128683; Exact path download failed: <span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>resp</span>.<span class=pl-c1>status_code</span><span class=pl-kos>}</span></span> - <span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>resp</span>.<span class=pl-c1>text</span><span class=pl-kos>}</span></span>&amp;quot;</span>)</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/daef76c7ca39955f7f105fffcc342d28/raw/182c6528dd626099cead123827576445e38700fb/download_file_for_user1.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/daef76c7ca39955f7f105fffcc342d28#file-download_file_for_user1-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          download_file_for_user1.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-110350e56c50.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-110350e56c50.css"><div id="gist139904496" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-download_file_for_user1-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="8" data-paste-markdown-skip="" data-tagsearch-path="download_file_for_user1.py">
        <tbody><tr>
          <td id="file-download_file_for_user1-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-download_file_for_user1-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-c"># Download file for user to bot</span></td>
        </tr>
        <tr>
          <td id="file-download_file_for_user1-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-download_file_for_user1-py-LC2" class="blob-code blob-code-inner js-file-line"><span class="pl-c"># I apologize to future developers who have to maintain this code. </span></td>
        </tr>
        <tr>
          <td id="file-download_file_for_user1-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-download_file_for_user1-py-LC3" class="blob-code blob-code-inner js-file-line"><span class="pl-c"># I can only blame the byzantine nature of the Microsoft Graph API and Teams API, </span></td>
        </tr>
        <tr>
          <td id="file-download_file_for_user1-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-download_file_for_user1-py-LC4" class="blob-code blob-code-inner js-file-line"><span class="pl-c"># which stores files in whatever location it feels like, and then </span></td>
        </tr>
        <tr>
          <td id="file-download_file_for_user1-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-download_file_for_user1-py-LC5" class="blob-code blob-code-inner js-file-line"><span class="pl-c"># provides a content URL that may or may not work.</span></td>
        </tr>
        <tr>
          <td id="file-download_file_for_user1-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-download_file_for_user1-py-LC6" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-s1">download_file_for_user</span>(<span class="pl-s1">file_name</span>, <span class="pl-s1">file_url</span>, <span class="pl-s1">headers</span>):</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user1-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-download_file_for_user1-py-LC7" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># If file is shared in a Team channel, the file is stored in sharepoint for the Team</span></td>
        </tr>
        <tr>
          <td id="file-download_file_for_user1-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-download_file_for_user1-py-LC8" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># We need to decode and fetch a great deal of info about sharepoint to fetch the file</span></td>
        </tr>
        <tr>
          <td id="file-download_file_for_user1-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-download_file_for_user1-py-LC9" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">try</span>:</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user1-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-download_file_for_user1-py-LC10" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">if</span> <span class="pl-s">".sharepoint.com/sites/"</span> <span class="pl-c1">in</span> <span class="pl-s1">file_url</span>:</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user1-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-download_file_for_user1-py-LC11" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user1-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-download_file_for_user1-py-LC12" class="blob-code blob-code-inner js-file-line">            <span class="pl-c"># Parse SharePoint URL</span></td>
        </tr>
        <tr>
          <td id="file-download_file_for_user1-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-download_file_for_user1-py-LC13" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">parts</span> <span class="pl-c1">=</span> <span class="pl-s1">file_url</span>.<span class="pl-c1">split</span>(<span class="pl-s">".sharepoint.com/sites/"</span>)</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user1-py-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-download_file_for_user1-py-LC14" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">hostname</span> <span class="pl-c1">=</span> <span class="pl-s1">parts</span>[<span class="pl-c1">0</span>].<span class="pl-c1">replace</span>(<span class="pl-s">"https://"</span>, <span class="pl-s">""</span>) <span class="pl-c1">+</span> <span class="pl-s">".sharepoint.com"</span></td>
        </tr>
        <tr>
          <td id="file-download_file_for_user1-py-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-download_file_for_user1-py-LC15" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">site_path_and_file</span> <span class="pl-c1">=</span> <span class="pl-s1">parts</span>[<span class="pl-c1">1</span>]</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user1-py-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-download_file_for_user1-py-LC16" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">site_name</span> <span class="pl-c1">=</span> <span class="pl-s1">site_path_and_file</span>.<span class="pl-c1">split</span>(<span class="pl-s">"/"</span>)[<span class="pl-c1">0</span>]</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user1-py-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-download_file_for_user1-py-LC17" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">file_relative_path_raw</span> <span class="pl-c1">=</span> <span class="pl-s1">site_path_and_file</span>[<span class="pl-en">len</span>(<span class="pl-s1">site_name</span>)<span class="pl-c1">+</span><span class="pl-c1">1</span>:]</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user1-py-L18" class="blob-num js-line-number js-blob-rnum" data-line-number="18"></td>
          <td id="file-download_file_for_user1-py-LC18" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user1-py-L19" class="blob-num js-line-number js-blob-rnum" data-line-number="19"></td>
          <td id="file-download_file_for_user1-py-LC19" class="blob-code blob-code-inner js-file-line">            <span class="pl-c"># Get the SharePoint site ID</span></td>
        </tr>
        <tr>
          <td id="file-download_file_for_user1-py-L20" class="blob-num js-line-number js-blob-rnum" data-line-number="20"></td>
          <td id="file-download_file_for_user1-py-LC20" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">site_lookup_url</span> <span class="pl-c1">=</span> <span class="pl-s">f"https://graph.microsoft.com/v1.0/sites/<span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">hostname</span><span class="pl-kos">}</span></span>:/sites/<span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">site_name</span><span class="pl-kos">}</span></span>"</span></td>
        </tr>
        <tr>
          <td id="file-download_file_for_user1-py-L21" class="blob-num js-line-number js-blob-rnum" data-line-number="21"></td>
          <td id="file-download_file_for_user1-py-LC21" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">site_resp</span> <span class="pl-c1">=</span> <span class="pl-s1">requests</span>.<span class="pl-c1">get</span>(<span class="pl-s1">site_lookup_url</span>, <span class="pl-s1">headers</span><span class="pl-c1">=</span><span class="pl-s1">headers</span>)</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user1-py-L22" class="blob-num js-line-number js-blob-rnum" data-line-number="22"></td>
          <td id="file-download_file_for_user1-py-LC22" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">site_resp</span>.<span class="pl-c1">raise_for_status</span>()</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user1-py-L23" class="blob-num js-line-number js-blob-rnum" data-line-number="23"></td>
          <td id="file-download_file_for_user1-py-LC23" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">site_id</span> <span class="pl-c1">=</span> <span class="pl-s1">site_resp</span>.<span class="pl-c1">json</span>()[<span class="pl-s">"id"</span>]</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user1-py-L24" class="blob-num js-line-number js-blob-rnum" data-line-number="24"></td>
          <td id="file-download_file_for_user1-py-LC24" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user1-py-L25" class="blob-num js-line-number js-blob-rnum" data-line-number="25"></td>
          <td id="file-download_file_for_user1-py-LC25" class="blob-code blob-code-inner js-file-line">            <span class="pl-c"># Get default document library (drive) ID</span></td>
        </tr>
        <tr>
          <td id="file-download_file_for_user1-py-L26" class="blob-num js-line-number js-blob-rnum" data-line-number="26"></td>
          <td id="file-download_file_for_user1-py-LC26" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">drive_url</span> <span class="pl-c1">=</span> <span class="pl-s">f"https://graph.microsoft.com/v1.0/sites/<span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">site_id</span><span class="pl-kos">}</span></span>/drive"</span></td>
        </tr>
        <tr>
          <td id="file-download_file_for_user1-py-L27" class="blob-num js-line-number js-blob-rnum" data-line-number="27"></td>
          <td id="file-download_file_for_user1-py-LC27" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">drive_resp</span> <span class="pl-c1">=</span> <span class="pl-s1">requests</span>.<span class="pl-c1">get</span>(<span class="pl-s1">drive_url</span>, <span class="pl-s1">headers</span><span class="pl-c1">=</span><span class="pl-s1">headers</span>)</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user1-py-L28" class="blob-num js-line-number js-blob-rnum" data-line-number="28"></td>
          <td id="file-download_file_for_user1-py-LC28" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">drive_resp</span>.<span class="pl-c1">raise_for_status</span>()</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user1-py-L29" class="blob-num js-line-number js-blob-rnum" data-line-number="29"></td>
          <td id="file-download_file_for_user1-py-LC29" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">drive_id</span> <span class="pl-c1">=</span> <span class="pl-s1">drive_resp</span>.<span class="pl-c1">json</span>()[<span class="pl-s">"id"</span>]</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user1-py-L30" class="blob-num js-line-number js-blob-rnum" data-line-number="30"></td>
          <td id="file-download_file_for_user1-py-LC30" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user1-py-L31" class="blob-num js-line-number js-blob-rnum" data-line-number="31"></td>
          <td id="file-download_file_for_user1-py-LC31" class="blob-code blob-code-inner js-file-line">            <span class="pl-c"># Try the exact file path, there are sometimes invalid unicode characters like \u202f embedded</span></td>
        </tr>
        <tr>
          <td id="file-download_file_for_user1-py-L32" class="blob-num js-line-number js-blob-rnum" data-line-number="32"></td>
          <td id="file-download_file_for_user1-py-LC32" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">file_path_encoded</span> <span class="pl-c1">=</span> <span class="pl-s1">urllib</span>.<span class="pl-c1">parse</span>.<span class="pl-c1">quote</span>(<span class="pl-s1">file_relative_path_raw</span>, <span class="pl-s1">safe</span><span class="pl-c1">=</span><span class="pl-s">"/"</span>)</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user1-py-L33" class="blob-num js-line-number js-blob-rnum" data-line-number="33"></td>
          <td id="file-download_file_for_user1-py-LC33" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">graph_url</span> <span class="pl-c1">=</span> <span class="pl-s">f"https://graph.microsoft.com/v1.0/sites/<span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">site_id</span><span class="pl-kos">}</span></span>/drives/<span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">drive_id</span><span class="pl-kos">}</span></span>/root:/<span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">file_path_encoded</span><span class="pl-kos">}</span></span>:/content"</span></td>
        </tr>
        <tr>
          <td id="file-download_file_for_user1-py-L34" class="blob-num js-line-number js-blob-rnum" data-line-number="34"></td>
          <td id="file-download_file_for_user1-py-LC34" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">resp</span> <span class="pl-c1">=</span> <span class="pl-s1">requests</span>.<span class="pl-c1">get</span>(<span class="pl-s1">graph_url</span>, <span class="pl-s1">headers</span><span class="pl-c1">=</span><span class="pl-s1">headers</span>)</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user1-py-L35" class="blob-num js-line-number js-blob-rnum" data-line-number="35"></td>
          <td id="file-download_file_for_user1-py-LC35" class="blob-code blob-code-inner js-file-line">            <span class="pl-k">if</span> <span class="pl-s1">resp</span>.<span class="pl-c1">status_code</span> <span class="pl-c1">==</span> <span class="pl-c1">200</span>:</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user1-py-L36" class="blob-num js-line-number js-blob-rnum" data-line-number="36"></td>
          <td id="file-download_file_for_user1-py-LC36" class="blob-code blob-code-inner js-file-line">                <span class="pl-en">print</span>(<span class="pl-s">f"&#128994; Download succeeded from SharePoint site using exact path: <span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">graph_url</span><span class="pl-kos">}</span></span>"</span>)</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user1-py-L37" class="blob-num js-line-number js-blob-rnum" data-line-number="37"></td>
          <td id="file-download_file_for_user1-py-LC37" class="blob-code blob-code-inner js-file-line">                <span class="pl-k">return</span> <span class="pl-s1">resp</span>.<span class="pl-c1">content</span></td>
        </tr>
        <tr>
          <td id="file-download_file_for_user1-py-L38" class="blob-num js-line-number js-blob-rnum" data-line-number="38"></td>
          <td id="file-download_file_for_user1-py-LC38" class="blob-code blob-code-inner js-file-line">            <span class="pl-k">else</span>:</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user1-py-L39" class="blob-num js-line-number js-blob-rnum" data-line-number="39"></td>
          <td id="file-download_file_for_user1-py-LC39" class="blob-code blob-code-inner js-file-line">                <span class="pl-en">print</span>(<span class="pl-s">f"&#128683; Exact path download failed: <span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">resp</span>.<span class="pl-c1">status_code</span><span class="pl-kos">}</span></span> - <span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">resp</span>.<span class="pl-c1">text</span><span class="pl-kos">}</span></span>"</span>)</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/daef76c7ca39955f7f105fffcc342d28/raw/182c6528dd626099cead123827576445e38700fb/download_file_for_user1.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/daef76c7ca39955f7f105fffcc342d28#file-download_file_for_user1-py" class="Link--inTextBlock">
          download_file_for_user1.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>I noticed quite a few of my test downloads had a &#8220;\u202f&#8221; which is the unicode character encoding for a &#8220;narrow non-breaking space&#8221;. Why are those in the file path? I have absolutely no idea dude. But we have a whole block where we just strip that off and see if we can download the file again.</p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist139904618\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-download_file_for_user2-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;download_file_for_user2.py content, created by KyMidd on 01:27AM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;8\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;download_file_for_user2.py\&quot;>\n        <tr>\n          <td id=\&quot;file-download_file_for_user2-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-download_file_for_user2-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-s1>download_file_for_user</span>(<span class=pl-s1>file_name</span>, <span class=pl-s1>file_url</span>, <span class=pl-s1>headers</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user2-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-download_file_for_user2-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>try</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user2-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-download_file_for_user2-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>if</span> <span class=pl-s>&amp;quot;.sharepoint.com/sites/&amp;quot;</span> <span class=pl-c1>in</span> <span class=pl-s1>file_url</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user2-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-download_file_for_user2-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-c># ...</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user2-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-download_file_for_user2-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-c># Try to access the file after normalizing the path</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user2-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-download_file_for_user2-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>normalized_path</span> <span class=pl-c1>=</span> <span class=pl-s1>unicodedata</span>.<span class=pl-c1>normalize</span>(<span class=pl-s>&amp;quot;NFKC&amp;quot;</span>, <span class=pl-s1>file_relative_path_raw</span>).<span class=pl-c1>replace</span>(<span class=pl-s>&amp;#39;<span class=pl-cce>\\u202f</span>&amp;#39;</span>, <span class=pl-s>&amp;#39; &amp;#39;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user2-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-download_file_for_user2-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-k>if</span> <span class=pl-s1>normalized_path</span> <span class=pl-c1>!=</span> <span class=pl-s1>file_relative_path_raw</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user2-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-download_file_for_user2-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                <span class=pl-s1>normalized_encoded</span> <span class=pl-c1>=</span> <span class=pl-s1>urllib</span>.<span class=pl-c1>parse</span>.<span class=pl-c1>quote</span>(<span class=pl-s1>normalized_path</span>, <span class=pl-s1>safe</span><span class=pl-c1>=</span><span class=pl-s>&amp;quot;/&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user2-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-download_file_for_user2-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                <span class=pl-s1>graph_url</span> <span class=pl-c1>=</span> <span class=pl-s>f&amp;quot;https://graph.microsoft.com/v1.0/sites/<span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>site_id</span><span class=pl-kos>}</span></span>/drives/<span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>drive_id</span><span class=pl-kos>}</span></span>/root:/<span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>normalized_encoded</span><span class=pl-kos>}</span></span>:/content&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user2-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-download_file_for_user2-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                <span class=pl-en>print</span>(<span class=pl-s>f&amp;quot;&#128994; Attempting normalized path download URL: <span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>graph_url</span><span class=pl-kos>}</span></span>&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user2-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-download_file_for_user2-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                <span class=pl-s1>resp</span> <span class=pl-c1>=</span> <span class=pl-s1>requests</span>.<span class=pl-c1>get</span>(<span class=pl-s1>graph_url</span>, <span class=pl-s1>headers</span><span class=pl-c1>=</span><span class=pl-s1>headers</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user2-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-download_file_for_user2-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                <span class=pl-k>if</span> <span class=pl-s1>resp</span>.<span class=pl-c1>status_code</span> <span class=pl-c1>==</span> <span class=pl-c1>200</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user2-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-download_file_for_user2-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                    <span class=pl-en>print</span>(<span class=pl-s>f&amp;quot;&#128994; Download succeeded from SharePoint site using normalized path: <span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>graph_url</span><span class=pl-kos>}</span></span>&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user2-py-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-download_file_for_user2-py-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                    <span class=pl-k>return</span> <span class=pl-s1>resp</span>.<span class=pl-c1>content</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user2-py-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-download_file_for_user2-py-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                <span class=pl-k>else</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user2-py-L16\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;16\&quot;></td>\n          <td id=\&quot;file-download_file_for_user2-py-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                    <span class=pl-en>print</span>(<span class=pl-s>f&amp;quot;&#128683; Normalized path download failed: <span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>resp</span>.<span class=pl-c1>status_code</span><span class=pl-kos>}</span></span> - <span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>resp</span>.<span class=pl-c1>text</span><span class=pl-kos>}</span></span>&amp;quot;</span>)</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/2369b685b5ef52f2d7a7ec96f2051983/raw/5d74bb056feddc6231467ea753d9b4e0ab3854e8/download_file_for_user2.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/2369b685b5ef52f2d7a7ec96f2051983#file-download_file_for_user2-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          download_file_for_user2.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-110350e56c50.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-110350e56c50.css"><div id="gist139904618" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-download_file_for_user2-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="8" data-paste-markdown-skip="" data-tagsearch-path="download_file_for_user2.py">
        <tbody><tr>
          <td id="file-download_file_for_user2-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-download_file_for_user2-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-s1">download_file_for_user</span>(<span class="pl-s1">file_name</span>, <span class="pl-s1">file_url</span>, <span class="pl-s1">headers</span>):</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user2-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-download_file_for_user2-py-LC2" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">try</span>:</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user2-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-download_file_for_user2-py-LC3" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">if</span> <span class="pl-s">".sharepoint.com/sites/"</span> <span class="pl-c1">in</span> <span class="pl-s1">file_url</span>:</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user2-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-download_file_for_user2-py-LC4" class="blob-code blob-code-inner js-file-line">            <span class="pl-c"># ...</span></td>
        </tr>
        <tr>
          <td id="file-download_file_for_user2-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-download_file_for_user2-py-LC5" class="blob-code blob-code-inner js-file-line">            <span class="pl-c"># Try to access the file after normalizing the path</span></td>
        </tr>
        <tr>
          <td id="file-download_file_for_user2-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-download_file_for_user2-py-LC6" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">normalized_path</span> <span class="pl-c1">=</span> <span class="pl-s1">unicodedata</span>.<span class="pl-c1">normalize</span>(<span class="pl-s">"NFKC"</span>, <span class="pl-s1">file_relative_path_raw</span>).<span class="pl-c1">replace</span>(<span class="pl-s">'<span class="pl-cce">\u202f</span>'</span>, <span class="pl-s">' '</span>)</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user2-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-download_file_for_user2-py-LC7" class="blob-code blob-code-inner js-file-line">            <span class="pl-k">if</span> <span class="pl-s1">normalized_path</span> <span class="pl-c1">!=</span> <span class="pl-s1">file_relative_path_raw</span>:</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user2-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-download_file_for_user2-py-LC8" class="blob-code blob-code-inner js-file-line">                <span class="pl-s1">normalized_encoded</span> <span class="pl-c1">=</span> <span class="pl-s1">urllib</span>.<span class="pl-c1">parse</span>.<span class="pl-c1">quote</span>(<span class="pl-s1">normalized_path</span>, <span class="pl-s1">safe</span><span class="pl-c1">=</span><span class="pl-s">"/"</span>)</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user2-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-download_file_for_user2-py-LC9" class="blob-code blob-code-inner js-file-line">                <span class="pl-s1">graph_url</span> <span class="pl-c1">=</span> <span class="pl-s">f"https://graph.microsoft.com/v1.0/sites/<span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">site_id</span><span class="pl-kos">}</span></span>/drives/<span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">drive_id</span><span class="pl-kos">}</span></span>/root:/<span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">normalized_encoded</span><span class="pl-kos">}</span></span>:/content"</span></td>
        </tr>
        <tr>
          <td id="file-download_file_for_user2-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-download_file_for_user2-py-LC10" class="blob-code blob-code-inner js-file-line">                <span class="pl-en">print</span>(<span class="pl-s">f"&#128994; Attempting normalized path download URL: <span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">graph_url</span><span class="pl-kos">}</span></span>"</span>)</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user2-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-download_file_for_user2-py-LC11" class="blob-code blob-code-inner js-file-line">                <span class="pl-s1">resp</span> <span class="pl-c1">=</span> <span class="pl-s1">requests</span>.<span class="pl-c1">get</span>(<span class="pl-s1">graph_url</span>, <span class="pl-s1">headers</span><span class="pl-c1">=</span><span class="pl-s1">headers</span>)</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user2-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-download_file_for_user2-py-LC12" class="blob-code blob-code-inner js-file-line">                <span class="pl-k">if</span> <span class="pl-s1">resp</span>.<span class="pl-c1">status_code</span> <span class="pl-c1">==</span> <span class="pl-c1">200</span>:</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user2-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-download_file_for_user2-py-LC13" class="blob-code blob-code-inner js-file-line">                    <span class="pl-en">print</span>(<span class="pl-s">f"&#128994; Download succeeded from SharePoint site using normalized path: <span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">graph_url</span><span class="pl-kos">}</span></span>"</span>)</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user2-py-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-download_file_for_user2-py-LC14" class="blob-code blob-code-inner js-file-line">                    <span class="pl-k">return</span> <span class="pl-s1">resp</span>.<span class="pl-c1">content</span></td>
        </tr>
        <tr>
          <td id="file-download_file_for_user2-py-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-download_file_for_user2-py-LC15" class="blob-code blob-code-inner js-file-line">                <span class="pl-k">else</span>:</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user2-py-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-download_file_for_user2-py-LC16" class="blob-code blob-code-inner js-file-line">                    <span class="pl-en">print</span>(<span class="pl-s">f"&#128683; Normalized path download failed: <span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">resp</span>.<span class="pl-c1">status_code</span><span class="pl-kos">}</span></span> - <span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">resp</span>.<span class="pl-c1">text</span><span class="pl-kos">}</span></span>"</span>)</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/2369b685b5ef52f2d7a7ec96f2051983/raw/5d74bb056feddc6231467ea753d9b4e0ab3854e8/download_file_for_user2.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/2369b685b5ef52f2d7a7ec96f2051983#file-download_file_for_user2-py" class="Link--inTextBlock">
          download_file_for_user2.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>If that doesn&#8217;t work we want to throw our hands up and retire, but there&#8217;s one more method we can use - literally fricking searching SharePoint using graph for the file name, then reading through all the results, one by one, to see if any exactly match the file name. </p><p>This a slow, serialized, and generally bad way to get this working. However, it often works, so &#175;\_(&#12484;)_/&#175;</p><p>If that still fails, we just give up for SharePoint paths. I don&#8217;t know another method to find the file, other than the SharePoint/Teams API devs simply providing a link to the file (please goddess). </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist139904751\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-download_file_for_user3-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;download_file_for_user3.py content, created by KyMidd on 01:42AM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;8\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;download_file_for_user3.py\&quot;>\n        <tr>\n          <td id=\&quot;file-download_file_for_user3-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-download_file_for_user3-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-s1>download_file_for_user</span>(<span class=pl-s1>file_name</span>, <span class=pl-s1>file_url</span>, <span class=pl-s1>headers</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user3-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-download_file_for_user3-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># ...</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user3-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-download_file_for_user3-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>try</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user3-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-download_file_for_user3-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>if</span> <span class=pl-s>&amp;quot;.sharepoint.com/sites/&amp;quot;</span> <span class=pl-c1>in</span> <span class=pl-s1>file_url</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user3-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-download_file_for_user3-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-c># ...</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user3-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-download_file_for_user3-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-c># If that doesn&amp;#39;t work, do naive search of all files in the Teams sharepoint site</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user3-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-download_file_for_user3-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-c># If matching file found, download it</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user3-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-download_file_for_user3-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-c># This is a slow hack, but is the only method reliably working to access Teams/SharePoint hosted files</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user3-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-download_file_for_user3-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>search_url</span> <span class=pl-c1>=</span> <span class=pl-s>f&amp;quot;https://graph.microsoft.com/v1.0/drives/<span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>drive_id</span><span class=pl-kos>}</span></span>/root/search(q=&amp;#39;<span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>urllib</span>.<span class=pl-c1>parse</span>.<span class=pl-c1>quote</span>(<span class=pl-s1>file_name</span>)<span class=pl-kos>}</span></span>&amp;#39;)&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user3-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-download_file_for_user3-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-en>print</span>(<span class=pl-s>f&amp;quot;&#128994; Attempting fallback file search: <span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>search_url</span><span class=pl-kos>}</span></span>&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user3-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-download_file_for_user3-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>search_response</span> <span class=pl-c1>=</span> <span class=pl-s1>requests</span>.<span class=pl-c1>get</span>(<span class=pl-s1>search_url</span>, <span class=pl-s1>headers</span><span class=pl-c1>=</span><span class=pl-s1>headers</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user3-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-download_file_for_user3-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-k>if</span> <span class=pl-s1>search_response</span>.<span class=pl-c1>status_code</span> <span class=pl-c1>==</span> <span class=pl-c1>200</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user3-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-download_file_for_user3-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                <span class=pl-k>for</span> <span class=pl-s1>item</span> <span class=pl-c1>in</span> <span class=pl-s1>search_response</span>.<span class=pl-c1>json</span>().<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;value&amp;quot;</span>, []):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user3-py-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-download_file_for_user3-py-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                    </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user3-py-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-download_file_for_user3-py-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                    <span class=pl-k>if</span> <span class=pl-s1>item</span>.<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;name&amp;quot;</span>) <span class=pl-c1>==</span> <span class=pl-s1>file_name</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user3-py-L16\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;16\&quot;></td>\n          <td id=\&quot;file-download_file_for_user3-py-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                        <span class=pl-s1>item_id</span> <span class=pl-c1>=</span> <span class=pl-s1>item</span>[<span class=pl-s>&amp;quot;id&amp;quot;</span>]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user3-py-L17\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;17\&quot;></td>\n          <td id=\&quot;file-download_file_for_user3-py-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                        <span class=pl-s1>download_url</span> <span class=pl-c1>=</span> <span class=pl-s>f&amp;quot;https://graph.microsoft.com/v1.0/drives/<span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>drive_id</span><span class=pl-kos>}</span></span>/items/<span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>item_id</span><span class=pl-kos>}</span></span>/content&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user3-py-L18\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;18\&quot;></td>\n          <td id=\&quot;file-download_file_for_user3-py-LC18\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                        <span class=pl-s1>file_response</span> <span class=pl-c1>=</span> <span class=pl-s1>requests</span>.<span class=pl-c1>get</span>(<span class=pl-s1>download_url</span>, <span class=pl-s1>headers</span><span class=pl-c1>=</span><span class=pl-s1>headers</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user3-py-L19\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;19\&quot;></td>\n          <td id=\&quot;file-download_file_for_user3-py-LC19\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                        <span class=pl-k>if</span> <span class=pl-s1>file_response</span>.<span class=pl-c1>status_code</span> <span class=pl-c1>==</span> <span class=pl-c1>200</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user3-py-L20\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;20\&quot;></td>\n          <td id=\&quot;file-download_file_for_user3-py-LC20\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                            <span class=pl-en>print</span>(<span class=pl-s>f&amp;quot;&#128994; Download succeeded from SharePoint search by file name: <span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>download_url</span><span class=pl-kos>}</span></span>&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user3-py-L21\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;21\&quot;></td>\n          <td id=\&quot;file-download_file_for_user3-py-LC21\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                            <span class=pl-k>return</span> <span class=pl-s1>file_response</span>.<span class=pl-c1>content</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user3-py-L22\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;22\&quot;></td>\n          <td id=\&quot;file-download_file_for_user3-py-LC22\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                        <span class=pl-k>else</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user3-py-L23\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;23\&quot;></td>\n          <td id=\&quot;file-download_file_for_user3-py-LC23\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                            <span class=pl-en>print</span>(<span class=pl-s>f&amp;quot;&#128683; SharePoint item download failed: <span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>file_response</span>.<span class=pl-c1>status_code</span><span class=pl-kos>}</span></span> - <span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>file_response</span>.<span class=pl-c1>text</span><span class=pl-kos>}</span></span>&amp;quot;</span>)</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/e400ecba0838ecff562682e617a99e12/raw/b9375546910db8e02b348ef88a10089891c83c10/download_file_for_user3.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/e400ecba0838ecff562682e617a99e12#file-download_file_for_user3-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          download_file_for_user3.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-110350e56c50.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-110350e56c50.css"><div id="gist139904751" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-download_file_for_user3-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="8" data-paste-markdown-skip="" data-tagsearch-path="download_file_for_user3.py">
        <tbody><tr>
          <td id="file-download_file_for_user3-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-download_file_for_user3-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-s1">download_file_for_user</span>(<span class="pl-s1">file_name</span>, <span class="pl-s1">file_url</span>, <span class="pl-s1">headers</span>):</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user3-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-download_file_for_user3-py-LC2" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># ...</span></td>
        </tr>
        <tr>
          <td id="file-download_file_for_user3-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-download_file_for_user3-py-LC3" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">try</span>:</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user3-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-download_file_for_user3-py-LC4" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">if</span> <span class="pl-s">".sharepoint.com/sites/"</span> <span class="pl-c1">in</span> <span class="pl-s1">file_url</span>:</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user3-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-download_file_for_user3-py-LC5" class="blob-code blob-code-inner js-file-line">            <span class="pl-c"># ...</span></td>
        </tr>
        <tr>
          <td id="file-download_file_for_user3-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-download_file_for_user3-py-LC6" class="blob-code blob-code-inner js-file-line">            <span class="pl-c"># If that doesn't work, do naive search of all files in the Teams sharepoint site</span></td>
        </tr>
        <tr>
          <td id="file-download_file_for_user3-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-download_file_for_user3-py-LC7" class="blob-code blob-code-inner js-file-line">            <span class="pl-c"># If matching file found, download it</span></td>
        </tr>
        <tr>
          <td id="file-download_file_for_user3-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-download_file_for_user3-py-LC8" class="blob-code blob-code-inner js-file-line">            <span class="pl-c"># This is a slow hack, but is the only method reliably working to access Teams/SharePoint hosted files</span></td>
        </tr>
        <tr>
          <td id="file-download_file_for_user3-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-download_file_for_user3-py-LC9" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">search_url</span> <span class="pl-c1">=</span> <span class="pl-s">f"https://graph.microsoft.com/v1.0/drives/<span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">drive_id</span><span class="pl-kos">}</span></span>/root/search(q='<span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">urllib</span>.<span class="pl-c1">parse</span>.<span class="pl-c1">quote</span>(<span class="pl-s1">file_name</span>)<span class="pl-kos">}</span></span>')"</span></td>
        </tr>
        <tr>
          <td id="file-download_file_for_user3-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-download_file_for_user3-py-LC10" class="blob-code blob-code-inner js-file-line">            <span class="pl-en">print</span>(<span class="pl-s">f"&#128994; Attempting fallback file search: <span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">search_url</span><span class="pl-kos">}</span></span>"</span>)</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user3-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-download_file_for_user3-py-LC11" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">search_response</span> <span class="pl-c1">=</span> <span class="pl-s1">requests</span>.<span class="pl-c1">get</span>(<span class="pl-s1">search_url</span>, <span class="pl-s1">headers</span><span class="pl-c1">=</span><span class="pl-s1">headers</span>)</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user3-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-download_file_for_user3-py-LC12" class="blob-code blob-code-inner js-file-line">            <span class="pl-k">if</span> <span class="pl-s1">search_response</span>.<span class="pl-c1">status_code</span> <span class="pl-c1">==</span> <span class="pl-c1">200</span>:</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user3-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-download_file_for_user3-py-LC13" class="blob-code blob-code-inner js-file-line">                <span class="pl-k">for</span> <span class="pl-s1">item</span> <span class="pl-c1">in</span> <span class="pl-s1">search_response</span>.<span class="pl-c1">json</span>().<span class="pl-c1">get</span>(<span class="pl-s">"value"</span>, []):</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user3-py-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-download_file_for_user3-py-LC14" class="blob-code blob-code-inner js-file-line">                    </td>
        </tr>
        <tr>
          <td id="file-download_file_for_user3-py-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-download_file_for_user3-py-LC15" class="blob-code blob-code-inner js-file-line">                    <span class="pl-k">if</span> <span class="pl-s1">item</span>.<span class="pl-c1">get</span>(<span class="pl-s">"name"</span>) <span class="pl-c1">==</span> <span class="pl-s1">file_name</span>:</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user3-py-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-download_file_for_user3-py-LC16" class="blob-code blob-code-inner js-file-line">                        <span class="pl-s1">item_id</span> <span class="pl-c1">=</span> <span class="pl-s1">item</span>[<span class="pl-s">"id"</span>]</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user3-py-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-download_file_for_user3-py-LC17" class="blob-code blob-code-inner js-file-line">                        <span class="pl-s1">download_url</span> <span class="pl-c1">=</span> <span class="pl-s">f"https://graph.microsoft.com/v1.0/drives/<span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">drive_id</span><span class="pl-kos">}</span></span>/items/<span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">item_id</span><span class="pl-kos">}</span></span>/content"</span></td>
        </tr>
        <tr>
          <td id="file-download_file_for_user3-py-L18" class="blob-num js-line-number js-blob-rnum" data-line-number="18"></td>
          <td id="file-download_file_for_user3-py-LC18" class="blob-code blob-code-inner js-file-line">                        <span class="pl-s1">file_response</span> <span class="pl-c1">=</span> <span class="pl-s1">requests</span>.<span class="pl-c1">get</span>(<span class="pl-s1">download_url</span>, <span class="pl-s1">headers</span><span class="pl-c1">=</span><span class="pl-s1">headers</span>)</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user3-py-L19" class="blob-num js-line-number js-blob-rnum" data-line-number="19"></td>
          <td id="file-download_file_for_user3-py-LC19" class="blob-code blob-code-inner js-file-line">                        <span class="pl-k">if</span> <span class="pl-s1">file_response</span>.<span class="pl-c1">status_code</span> <span class="pl-c1">==</span> <span class="pl-c1">200</span>:</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user3-py-L20" class="blob-num js-line-number js-blob-rnum" data-line-number="20"></td>
          <td id="file-download_file_for_user3-py-LC20" class="blob-code blob-code-inner js-file-line">                            <span class="pl-en">print</span>(<span class="pl-s">f"&#128994; Download succeeded from SharePoint search by file name: <span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">download_url</span><span class="pl-kos">}</span></span>"</span>)</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user3-py-L21" class="blob-num js-line-number js-blob-rnum" data-line-number="21"></td>
          <td id="file-download_file_for_user3-py-LC21" class="blob-code blob-code-inner js-file-line">                            <span class="pl-k">return</span> <span class="pl-s1">file_response</span>.<span class="pl-c1">content</span></td>
        </tr>
        <tr>
          <td id="file-download_file_for_user3-py-L22" class="blob-num js-line-number js-blob-rnum" data-line-number="22"></td>
          <td id="file-download_file_for_user3-py-LC22" class="blob-code blob-code-inner js-file-line">                        <span class="pl-k">else</span>:</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user3-py-L23" class="blob-num js-line-number js-blob-rnum" data-line-number="23"></td>
          <td id="file-download_file_for_user3-py-LC23" class="blob-code blob-code-inner js-file-line">                            <span class="pl-en">print</span>(<span class="pl-s">f"&#128683; SharePoint item download failed: <span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">file_response</span>.<span class="pl-c1">status_code</span><span class="pl-kos">}</span></span> - <span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">file_response</span>.<span class="pl-c1">text</span><span class="pl-kos">}</span></span>"</span>)</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/e400ecba0838ecff562682e617a99e12/raw/b9375546910db8e02b348ef88a10089891c83c10/download_file_for_user3.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/e400ecba0838ecff562682e617a99e12#file-download_file_for_user3-py" class="Link--inTextBlock">
          download_file_for_user3.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>Next up, we try to download the file from the user&#8217;s personal OneDrive. This works if the user has uploaded the file themselves in a DM context. </p><p>Again, this is a relatively &#8220;bad&#8221; solution since it requires our own user to have uploaded the file. If the file was uploaded in a DM context by another user, I think we just won&#8217;t find it. Sigh. </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist139904785\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-download_file_for_user4-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;download_file_for_user4.py content, created by KyMidd on 01:46AM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;8\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;download_file_for_user4.py\&quot;>\n        <tr>\n          <td id=\&quot;file-download_file_for_user4-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-download_file_for_user4-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-s1>download_file_for_user</span>(<span class=pl-s1>file_name</span>, <span class=pl-s1>file_url</span>, <span class=pl-s1>headers</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user4-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-download_file_for_user4-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>try</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user4-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-download_file_for_user4-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>if</span> <span class=pl-s>&amp;quot;.sharepoint.com/sites/&amp;quot;</span> <span class=pl-c1>in</span> <span class=pl-s1>file_url</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user4-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-download_file_for_user4-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-c>#...</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user4-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-download_file_for_user4-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># If files are shared in a Teams chat, they are stored in the user&amp;#39;s OneDrive under &amp;quot;Microsoft Teams Chat Files&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user4-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-download_file_for_user4-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># This is the method most often used for 1:1 chats or group chats</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user4-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-download_file_for_user4-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>try</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user4-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-download_file_for_user4-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>base_path</span> <span class=pl-c1>=</span> <span class=pl-s>f&amp;quot;Microsoft Teams Chat Files/<span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>file_name</span><span class=pl-kos>}</span></span>&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user4-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-download_file_for_user4-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>encoded_path</span> <span class=pl-c1>=</span> <span class=pl-s1>urllib</span>.<span class=pl-c1>parse</span>.<span class=pl-c1>quote</span>(<span class=pl-s1>base_path</span>, <span class=pl-s1>safe</span><span class=pl-c1>=</span><span class=pl-s>&amp;quot;/&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user4-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-download_file_for_user4-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>graph_url</span> <span class=pl-c1>=</span> <span class=pl-s>f&amp;quot;https://graph.microsoft.com/v1.0/me/drive/root:/<span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>encoded_path</span><span class=pl-kos>}</span></span>:/content&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user4-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-download_file_for_user4-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>response</span> <span class=pl-c1>=</span> <span class=pl-s1>requests</span>.<span class=pl-c1>get</span>(<span class=pl-s1>graph_url</span>, <span class=pl-s1>headers</span><span class=pl-c1>=</span><span class=pl-s1>headers</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user4-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-download_file_for_user4-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>if</span> <span class=pl-s1>response</span>.<span class=pl-c1>status_code</span> <span class=pl-c1>==</span> <span class=pl-c1>200</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user4-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-download_file_for_user4-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-en>print</span>(<span class=pl-s>f&amp;quot;&#128994; Download succeeded from user&amp;#39;s OneDrive: <span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>graph_url</span><span class=pl-kos>}</span></span>&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user4-py-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-download_file_for_user4-py-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-k>return</span> <span class=pl-s1>response</span>.<span class=pl-c1>content</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user4-py-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-download_file_for_user4-py-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>else</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user4-py-L16\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;16\&quot;></td>\n          <td id=\&quot;file-download_file_for_user4-py-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-en>print</span>(<span class=pl-s>f&amp;quot;&#128683; OneDrive download failed: <span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>response</span>.<span class=pl-c1>status_code</span><span class=pl-kos>}</span></span> - <span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>response</span>.<span class=pl-c1>text</span><span class=pl-kos>}</span></span>&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user4-py-L17\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;17\&quot;></td>\n          <td id=\&quot;file-download_file_for_user4-py-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>except</span> <span class=pl-v>Exception</span> <span class=pl-k>as</span> <span class=pl-s1>e</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user4-py-L18\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;18\&quot;></td>\n          <td id=\&quot;file-download_file_for_user4-py-LC18\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-en>print</span>(<span class=pl-s>f&amp;quot;&#128683; OneDrive download error: <span class=pl-s1><span class=pl-kos>{</span><span class=pl-en>str</span>(<span class=pl-s1>e</span>)<span class=pl-kos>}</span></span>&amp;quot;</span>)</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/ed7cbc4b4c11cd7a8c96de2953b6859f/raw/698f81b0032169cda581223e963059d5bc275427/download_file_for_user4.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/ed7cbc4b4c11cd7a8c96de2953b6859f#file-download_file_for_user4-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          download_file_for_user4.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-110350e56c50.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-110350e56c50.css"><div id="gist139904785" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-download_file_for_user4-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="8" data-paste-markdown-skip="" data-tagsearch-path="download_file_for_user4.py">
        <tbody><tr>
          <td id="file-download_file_for_user4-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-download_file_for_user4-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-s1">download_file_for_user</span>(<span class="pl-s1">file_name</span>, <span class="pl-s1">file_url</span>, <span class="pl-s1">headers</span>):</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user4-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-download_file_for_user4-py-LC2" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">try</span>:</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user4-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-download_file_for_user4-py-LC3" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">if</span> <span class="pl-s">".sharepoint.com/sites/"</span> <span class="pl-c1">in</span> <span class="pl-s1">file_url</span>:</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user4-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-download_file_for_user4-py-LC4" class="blob-code blob-code-inner js-file-line">            <span class="pl-c">#...</span></td>
        </tr>
        <tr>
          <td id="file-download_file_for_user4-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-download_file_for_user4-py-LC5" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># If files are shared in a Teams chat, they are stored in the user's OneDrive under "Microsoft Teams Chat Files"</span></td>
        </tr>
        <tr>
          <td id="file-download_file_for_user4-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-download_file_for_user4-py-LC6" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># This is the method most often used for 1:1 chats or group chats</span></td>
        </tr>
        <tr>
          <td id="file-download_file_for_user4-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-download_file_for_user4-py-LC7" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">try</span>:</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user4-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-download_file_for_user4-py-LC8" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">base_path</span> <span class="pl-c1">=</span> <span class="pl-s">f"Microsoft Teams Chat Files/<span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">file_name</span><span class="pl-kos">}</span></span>"</span></td>
        </tr>
        <tr>
          <td id="file-download_file_for_user4-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-download_file_for_user4-py-LC9" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">encoded_path</span> <span class="pl-c1">=</span> <span class="pl-s1">urllib</span>.<span class="pl-c1">parse</span>.<span class="pl-c1">quote</span>(<span class="pl-s1">base_path</span>, <span class="pl-s1">safe</span><span class="pl-c1">=</span><span class="pl-s">"/"</span>)</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user4-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-download_file_for_user4-py-LC10" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">graph_url</span> <span class="pl-c1">=</span> <span class="pl-s">f"https://graph.microsoft.com/v1.0/me/drive/root:/<span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">encoded_path</span><span class="pl-kos">}</span></span>:/content"</span></td>
        </tr>
        <tr>
          <td id="file-download_file_for_user4-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-download_file_for_user4-py-LC11" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">response</span> <span class="pl-c1">=</span> <span class="pl-s1">requests</span>.<span class="pl-c1">get</span>(<span class="pl-s1">graph_url</span>, <span class="pl-s1">headers</span><span class="pl-c1">=</span><span class="pl-s1">headers</span>)</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user4-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-download_file_for_user4-py-LC12" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">if</span> <span class="pl-s1">response</span>.<span class="pl-c1">status_code</span> <span class="pl-c1">==</span> <span class="pl-c1">200</span>:</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user4-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-download_file_for_user4-py-LC13" class="blob-code blob-code-inner js-file-line">            <span class="pl-en">print</span>(<span class="pl-s">f"&#128994; Download succeeded from user's OneDrive: <span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">graph_url</span><span class="pl-kos">}</span></span>"</span>)</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user4-py-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-download_file_for_user4-py-LC14" class="blob-code blob-code-inner js-file-line">            <span class="pl-k">return</span> <span class="pl-s1">response</span>.<span class="pl-c1">content</span></td>
        </tr>
        <tr>
          <td id="file-download_file_for_user4-py-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-download_file_for_user4-py-LC15" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">else</span>:</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user4-py-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-download_file_for_user4-py-LC16" class="blob-code blob-code-inner js-file-line">            <span class="pl-en">print</span>(<span class="pl-s">f"&#128683; OneDrive download failed: <span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">response</span>.<span class="pl-c1">status_code</span><span class="pl-kos">}</span></span> - <span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">response</span>.<span class="pl-c1">text</span><span class="pl-kos">}</span></span>"</span>)</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user4-py-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-download_file_for_user4-py-LC17" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">except</span> <span class="pl-v">Exception</span> <span class="pl-k">as</span> <span class="pl-s1">e</span>:</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user4-py-L18" class="blob-num js-line-number js-blob-rnum" data-line-number="18"></td>
          <td id="file-download_file_for_user4-py-LC18" class="blob-code blob-code-inner js-file-line">        <span class="pl-en">print</span>(<span class="pl-s">f"&#128683; OneDrive download error: <span class="pl-s1"><span class="pl-kos">{</span><span class="pl-en">str</span>(<span class="pl-s1">e</span>)<span class="pl-kos">}</span></span>"</span>)</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/ed7cbc4b4c11cd7a8c96de2953b6859f/raw/698f81b0032169cda581223e963059d5bc275427/download_file_for_user4.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/ed7cbc4b4c11cd7a8c96de2953b6859f#file-download_file_for_user4-py" class="Link--inTextBlock">
          download_file_for_user4.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>Lastly, we try to directly download the file via the file_url. You&#8217;d think this would be the most reliable method for downloading files. After all, this is the only method we use for slack and it works 100% of the time. </p><p>However, I have never seen this work in my testing and live auditing. I have no idea why, but I keep it because, well, it SHOULD work, right? And maybe it&#8217;ll catch some edge case that I haven&#8217;t tested yet. </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist139904822\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-download_file_for_user5-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;download_file_for_user5.py content, created by KyMidd on 01:49AM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;8\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;download_file_for_user5.py\&quot;>\n        <tr>\n          <td id=\&quot;file-download_file_for_user5-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-download_file_for_user5-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-s1>download_file_for_user</span>(<span class=pl-s1>file_name</span>, <span class=pl-s1>file_url</span>, <span class=pl-s1>headers</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user5-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-download_file_for_user5-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>try</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user5-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-download_file_for_user5-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>if</span> <span class=pl-s>&amp;quot;.sharepoint.com/sites/&amp;quot;</span> <span class=pl-c1>in</span> <span class=pl-s1>file_url</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user5-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-download_file_for_user5-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-c>#...</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user5-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-download_file_for_user5-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c>#...</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user5-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-download_file_for_user5-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Last resort, try to directly download the file from the content URL</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user5-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-download_file_for_user5-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># This sometimes works in 1:1 contexts, but fails so often it&amp;#39;s our last resort</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user5-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-download_file_for_user5-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>try</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user5-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-download_file_for_user5-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>response</span> <span class=pl-c1>=</span> <span class=pl-s1>requests</span>.<span class=pl-c1>get</span>(<span class=pl-s1>file_url</span>, <span class=pl-s1>headers</span><span class=pl-c1>=</span><span class=pl-s1>headers</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user5-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-download_file_for_user5-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>if</span> <span class=pl-s1>response</span>.<span class=pl-c1>status_code</span> <span class=pl-c1>==</span> <span class=pl-c1>200</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user5-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-download_file_for_user5-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-en>print</span>(<span class=pl-s>f&amp;quot;Download succeeded from direct URL: <span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>file_url</span><span class=pl-kos>}</span></span>&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user5-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-download_file_for_user5-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-k>return</span> <span class=pl-s1>response</span>.<span class=pl-c1>content</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user5-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-download_file_for_user5-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>else</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user5-py-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-download_file_for_user5-py-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-en>print</span>(<span class=pl-s>f&amp;quot;&#128683; Direct URL download failed: <span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>response</span>.<span class=pl-c1>status_code</span><span class=pl-kos>}</span></span> - <span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>response</span>.<span class=pl-c1>text</span><span class=pl-kos>}</span></span>&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user5-py-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-download_file_for_user5-py-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>except</span> <span class=pl-v>Exception</span> <span class=pl-k>as</span> <span class=pl-s1>e</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user5-py-L16\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;16\&quot;></td>\n          <td id=\&quot;file-download_file_for_user5-py-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-en>print</span>(<span class=pl-s>f&amp;quot;&#128683; Direct URL download error: <span class=pl-s1><span class=pl-kos>{</span><span class=pl-en>str</span>(<span class=pl-s1>e</span>)<span class=pl-kos>}</span></span>&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user5-py-L17\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;17\&quot;></td>\n          <td id=\&quot;file-download_file_for_user5-py-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user5-py-L18\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;18\&quot;></td>\n          <td id=\&quot;file-download_file_for_user5-py-LC18\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-en>print</span>(<span class=pl-s>f&amp;quot;&#128683; Could not download file <span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>file_name</span><span class=pl-kos>}</span></span>&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-download_file_for_user5-py-L19\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;19\&quot;></td>\n          <td id=\&quot;file-download_file_for_user5-py-LC19\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>return</span> <span class=pl-c1>None</span></td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/86e8485abb5d629151014589fe06446f/raw/7fd26151e9dcfd530441268bf2c25e2699bd34c5/download_file_for_user5.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/86e8485abb5d629151014589fe06446f#file-download_file_for_user5-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          download_file_for_user5.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-110350e56c50.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-110350e56c50.css"><div id="gist139904822" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-download_file_for_user5-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="8" data-paste-markdown-skip="" data-tagsearch-path="download_file_for_user5.py">
        <tbody><tr>
          <td id="file-download_file_for_user5-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-download_file_for_user5-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-s1">download_file_for_user</span>(<span class="pl-s1">file_name</span>, <span class="pl-s1">file_url</span>, <span class="pl-s1">headers</span>):</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user5-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-download_file_for_user5-py-LC2" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">try</span>:</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user5-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-download_file_for_user5-py-LC3" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">if</span> <span class="pl-s">".sharepoint.com/sites/"</span> <span class="pl-c1">in</span> <span class="pl-s1">file_url</span>:</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user5-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-download_file_for_user5-py-LC4" class="blob-code blob-code-inner js-file-line">            <span class="pl-c">#...</span></td>
        </tr>
        <tr>
          <td id="file-download_file_for_user5-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-download_file_for_user5-py-LC5" class="blob-code blob-code-inner js-file-line">    <span class="pl-c">#...</span></td>
        </tr>
        <tr>
          <td id="file-download_file_for_user5-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-download_file_for_user5-py-LC6" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Last resort, try to directly download the file from the content URL</span></td>
        </tr>
        <tr>
          <td id="file-download_file_for_user5-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-download_file_for_user5-py-LC7" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># This sometimes works in 1:1 contexts, but fails so often it's our last resort</span></td>
        </tr>
        <tr>
          <td id="file-download_file_for_user5-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-download_file_for_user5-py-LC8" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">try</span>:</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user5-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-download_file_for_user5-py-LC9" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">response</span> <span class="pl-c1">=</span> <span class="pl-s1">requests</span>.<span class="pl-c1">get</span>(<span class="pl-s1">file_url</span>, <span class="pl-s1">headers</span><span class="pl-c1">=</span><span class="pl-s1">headers</span>)</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user5-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-download_file_for_user5-py-LC10" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">if</span> <span class="pl-s1">response</span>.<span class="pl-c1">status_code</span> <span class="pl-c1">==</span> <span class="pl-c1">200</span>:</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user5-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-download_file_for_user5-py-LC11" class="blob-code blob-code-inner js-file-line">            <span class="pl-en">print</span>(<span class="pl-s">f"Download succeeded from direct URL: <span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">file_url</span><span class="pl-kos">}</span></span>"</span>)</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user5-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-download_file_for_user5-py-LC12" class="blob-code blob-code-inner js-file-line">            <span class="pl-k">return</span> <span class="pl-s1">response</span>.<span class="pl-c1">content</span></td>
        </tr>
        <tr>
          <td id="file-download_file_for_user5-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-download_file_for_user5-py-LC13" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">else</span>:</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user5-py-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-download_file_for_user5-py-LC14" class="blob-code blob-code-inner js-file-line">            <span class="pl-en">print</span>(<span class="pl-s">f"&#128683; Direct URL download failed: <span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">response</span>.<span class="pl-c1">status_code</span><span class="pl-kos">}</span></span> - <span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">response</span>.<span class="pl-c1">text</span><span class="pl-kos">}</span></span>"</span>)</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user5-py-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-download_file_for_user5-py-LC15" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">except</span> <span class="pl-v">Exception</span> <span class="pl-k">as</span> <span class="pl-s1">e</span>:</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user5-py-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-download_file_for_user5-py-LC16" class="blob-code blob-code-inner js-file-line">        <span class="pl-en">print</span>(<span class="pl-s">f"&#128683; Direct URL download error: <span class="pl-s1"><span class="pl-kos">{</span><span class="pl-en">str</span>(<span class="pl-s1">e</span>)<span class="pl-kos">}</span></span>"</span>)</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user5-py-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-download_file_for_user5-py-LC17" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user5-py-L18" class="blob-num js-line-number js-blob-rnum" data-line-number="18"></td>
          <td id="file-download_file_for_user5-py-LC18" class="blob-code blob-code-inner js-file-line">    <span class="pl-en">print</span>(<span class="pl-s">f"&#128683; Could not download file <span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">file_name</span><span class="pl-kos">}</span></span>"</span>)</td>
        </tr>
        <tr>
          <td id="file-download_file_for_user5-py-L19" class="blob-num js-line-number js-blob-rnum" data-line-number="19"></td>
          <td id="file-download_file_for_user5-py-LC19" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">return</span> <span class="pl-c1">None</span></td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/86e8485abb5d629151014589fe06446f/raw/7fd26151e9dcfd530441268bf2c25e2699bd34c5/download_file_for_user5.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/86e8485abb5d629151014589fe06446f#file-download_file_for_user5-py" class="Link--inTextBlock">
          download_file_for_user5.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><h1>Finish the Conversation</h1><p>After that, the logic is pretty much the same as the Slack Vera bot - we attach the bytecode of the files to the conversation turns, pack each one with metadata to identify it as &#8220;user&#8221; or &#8220;assistant&#8221;, and pass it to the model. </p><p>The model returns a response, and we pass it to the Teams chat as a response. </p><p>It&#8217;s quite a bit slower than the Slack Vera, largely due to the vastly greater number of API requests required to do pretty much anything - finding the conversation ID, mapping different types of identifiers together, finding and testing all different paths for downloading files, etc. </p><p>It&#8217;s hard to beat Teams&#8217; price (FREE, or at least table stakes in your <span class="cashtag-wrap" data-attrs="{&quot;symbol&quot;:&quot;$MSFT&quot;}" data-component-name="CashtagToDOM"></span> EA agreements), but it remains a bear to work with. Thank goodness it&#8217;s improving, it was utterly awful a few years ago. </p><h1>Summary</h1><p>We covered even further the idiosyncrasies of the various APIs that Teams uses, and how they basically operate like a raccoon in a trench coat - lots of loosely collected APIs built in different standards and with differently methodologies, trying to operate as a single entity. It sometimes even works properly. </p><p>That&#8217;s probably it for the Teams Vera series. I&#8217;ve discovered <a href="https://strandsagents.com/latest/">Strands</a>, an open source tool from AWS that permits building MCP-enabled Agents to do stuff in a standard way, and I&#8217;m entirely hooked. I&#8217;m barely keeping my non-AI job right now, this stuff is just too cool.</p><p>My next articles will surely follow that trend. </p><p>Good luck out there. <br>kyler</p>]]></content:encoded></item><item><title><![CDATA[🔥Building a Teams Bot with AI Capabilities - Part 5 - GenAI Integration with Teams🔥 ]]></title><description><![CDATA[aka, what are all these GUIDs and why don't these APIs exist?]]></description><link>https://www.letsdodevops.com/p/teamsai-5</link><guid isPermaLink="false">https://www.letsdodevops.com/p/teamsai-5</guid><dc:creator><![CDATA[Kyler Middleton]]></dc:creator><pubDate>Tue, 29 Jul 2025 14:02:43 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!nbAj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ef0e7cb-70d5-4780-bde4-16d90f0cc74d_2040x1448.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote><p><em>This blog series focuses on presenting complex DevOps projects as simple and approachable via plain language and lots of pictures. You can do it!</em></p><p>These articles are supported by readers, <a href="https://www.letsdodevops.com/subscribe">please consider subscribing</a> to support me writing more of these articles &lt;3 :)</p><p>This article is part of a series of articles, because 1 article would be absolutely massive.</p></blockquote><ul><li><p>Part 1: <a href="https://www.letsdodevops.com/p/ai-teams-bot">Create an Azure Bot and App Registration</a></p></li><li><p>Part 2: <a href="https://www.letsdodevops.com/p/teamsai-2">Register Bot in Teams with Teams Developer Portal</a></p></li><li><p>Part 3: <a href="https://www.letsdodevops.com/p/teamsai-3">Delegated Permissions and Making Lambda Stateful for Oauth2</a></p></li><li><p>Part 4: <a href="https://www.letsdodevops.com/p/teamsai-4">Building the Receiver lambda to store tokens and state</a></p></li><li><p>Part 5 (this article): Finding messages, reading conversations in Teams</p></li><li><p>Part 6: <a href="https://www.letsdodevops.com/p/teamsai-6">Finding attachments and reading them into Bedrock</a></p></li></ul><p>Hey all!</p><p>In the last article, we covered how the Receiver lambda gets an OAuth2 token that&#8217;s compatible with the GraphAPI, and how we encrypt and store that token so that our Worker lambda can utilize it to DO STUFF - like have AI conversations and post responses from our models back to the Teams platform. </p><p>Assuming that&#8217;s all working, each time our Worker is triggered now, we&#8217;re going to get a valid OAuth2 token, and some conversation information (someone tagged the bot, or messaged it directly), and it&#8217;s now our Worker lambda&#8217;s job to do the AI magic. There&#8217;s a few parts of AI magic we&#8217;ll cover. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!nbAj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ef0e7cb-70d5-4780-bde4-16d90f0cc74d_2040x1448.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!nbAj!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ef0e7cb-70d5-4780-bde4-16d90f0cc74d_2040x1448.png 424w, https://substackcdn.com/image/fetch/$s_!nbAj!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ef0e7cb-70d5-4780-bde4-16d90f0cc74d_2040x1448.png 848w, https://substackcdn.com/image/fetch/$s_!nbAj!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ef0e7cb-70d5-4780-bde4-16d90f0cc74d_2040x1448.png 1272w, https://substackcdn.com/image/fetch/$s_!nbAj!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ef0e7cb-70d5-4780-bde4-16d90f0cc74d_2040x1448.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!nbAj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ef0e7cb-70d5-4780-bde4-16d90f0cc74d_2040x1448.png" width="1456" height="1033" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4ef0e7cb-70d5-4780-bde4-16d90f0cc74d_2040x1448.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1033,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:479000,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.letsdodevops.com/i/168320556?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ef0e7cb-70d5-4780-bde4-16d90f0cc74d_2040x1448.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!nbAj!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ef0e7cb-70d5-4780-bde4-16d90f0cc74d_2040x1448.png 424w, https://substackcdn.com/image/fetch/$s_!nbAj!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ef0e7cb-70d5-4780-bde4-16d90f0cc74d_2040x1448.png 848w, https://substackcdn.com/image/fetch/$s_!nbAj!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ef0e7cb-70d5-4780-bde4-16d90f0cc74d_2040x1448.png 1272w, https://substackcdn.com/image/fetch/$s_!nbAj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ef0e7cb-70d5-4780-bde4-16d90f0cc74d_2040x1448.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><ul><li><p><strong>Extract the message context</strong> - is this a DM or tag in a Teams.. Team (We know naming stuff is hard $msft)</p></li><li><p><strong>Read the previous messages if applicable</strong></p><ul><li><p>In a DM context, there&#8217;s no good way yet to differentiate different conversations, so we just respond directly to each question - there&#8217;s no follow-up/thread model. When Teams supports this (is that coming?) we could extend the bot to cover that model. </p></li><li><p>In a Teams Team context, read messages back. We have to limit this to some length, and not read ALL the messages if there&#8217;s hundreds. So we limit to like 20 messages. </p></li></ul></li><li><p><strong>Download any attachments to any message we read</strong></p><ul><li><p>This is hilariously very different on context because of how Teams doesn&#8217;t REALLY exist. If you share an attachment in a DM, it&#8217;s stored in your OneDrive. If you share an attachment in a Teams Team, it&#8217;s usually stored in that Team&#8217;s SharePoint storage. The names are sometimes transformed to be SharePoint/OneDrive compatible, which makes my pull my hair out. We&#8217;ll cover this really well lower on. </p></li></ul></li><li><p><strong>Read the user data of any post author</strong> </p><ul><li><p>To detect pronouns, nicknames, etc. </p></li></ul></li><li><p><strong>Get an AI response</strong></p><ul><li><p>We&#8217;ve covered this well in previous articles (<a href="https://www.letsdodevops.com/t/ai-slack-bot">here&#8217;s the SlackBot articles</a>), so we won&#8217;t cover this here</p></li></ul></li><li><p><strong>Post a response</strong></p><ul><li><p>Send our response back, properly formatted for Teams. </p></li></ul></li></ul><p>When it all works, it looks like this:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!D2FM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5976dda-cb5f-4896-a612-a57031773a38_2390x1318.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!D2FM!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5976dda-cb5f-4896-a612-a57031773a38_2390x1318.png 424w, https://substackcdn.com/image/fetch/$s_!D2FM!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5976dda-cb5f-4896-a612-a57031773a38_2390x1318.png 848w, https://substackcdn.com/image/fetch/$s_!D2FM!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5976dda-cb5f-4896-a612-a57031773a38_2390x1318.png 1272w, https://substackcdn.com/image/fetch/$s_!D2FM!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5976dda-cb5f-4896-a612-a57031773a38_2390x1318.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!D2FM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5976dda-cb5f-4896-a612-a57031773a38_2390x1318.png" width="1456" height="803" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c5976dda-cb5f-4896-a612-a57031773a38_2390x1318.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:803,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:385540,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.letsdodevops.com/i/168320556?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5976dda-cb5f-4896-a612-a57031773a38_2390x1318.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!D2FM!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5976dda-cb5f-4896-a612-a57031773a38_2390x1318.png 424w, https://substackcdn.com/image/fetch/$s_!D2FM!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5976dda-cb5f-4896-a612-a57031773a38_2390x1318.png 848w, https://substackcdn.com/image/fetch/$s_!D2FM!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5976dda-cb5f-4896-a612-a57031773a38_2390x1318.png 1272w, https://substackcdn.com/image/fetch/$s_!D2FM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5976dda-cb5f-4896-a612-a57031773a38_2390x1318.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>If you don&#8217;t want to walk through all this, the code we&#8217;re going to be talking about is here: </p><blockquote><p><a href="http://github.com/KyMidd/TeamsAIBot/blob/master/lambda/src/verateams.py">github.com/KyMidd/TeamsAIBot</a></p></blockquote><h1>Bot Bearer Token Fetching</h1><p>Lets first fetch the bot&#8217;s bearer token. That token is used to post responses to the user in 1:1 DM chats, and respond to a Team&#8217;s Team/Channel in shared context. </p><p>On line 4 we fetch the json-structured secrets package, and then load it on line 7. Lines 8 - 10, we assign particular values of the secrets to variables for easy use. </p><p>Then on line 13, we call get_teams_bearer_token and send most of our secret info to it. Lets follow that thread. </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist139607894\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-lambda_handler1-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;lambda_handler1.py content, created by KyMidd on 12:42AM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;8\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;lambda_handler1.py\&quot;>\n        <tr>\n          <td id=\&quot;file-lambda_handler1-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-lambda_handler1-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-en>lambda_handler</span>(<span class=pl-s1>event</span>, <span class=pl-s1>context</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler1-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-lambda_handler1-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c>#... </span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler1-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-lambda_handler1-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Fetch secret package</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler1-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-lambda_handler1-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>secrets</span> <span class=pl-c1>=</span> <span class=pl-en>get_secret_ssm_layer</span>(<span class=pl-s1>bot_secret_name</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler1-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-lambda_handler1-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler1-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-lambda_handler1-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Disambiguate secrets</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler1-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-lambda_handler1-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>secrets_json</span> <span class=pl-c1>=</span> <span class=pl-s1>json</span>.<span class=pl-c1>loads</span>(<span class=pl-s1>secrets</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler1-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-lambda_handler1-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c1>TENANT_ID</span> <span class=pl-c1>=</span> <span class=pl-s1>secrets_json</span>[<span class=pl-s>&amp;quot;TENANT_ID&amp;quot;</span>]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler1-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-lambda_handler1-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c1>CLIENT_ID</span> <span class=pl-c1>=</span> <span class=pl-s1>secrets_json</span>[<span class=pl-s>&amp;quot;CLIENT_ID&amp;quot;</span>]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler1-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-lambda_handler1-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c1>CLIENT_SECRET</span> <span class=pl-c1>=</span> <span class=pl-s1>secrets_json</span>[<span class=pl-s>&amp;quot;CLIENT_SECRET&amp;quot;</span>]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler1-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-lambda_handler1-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler1-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-lambda_handler1-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Get bearer token for the bot to use to post messages</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler1-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-lambda_handler1-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>bot_bearer_token</span> <span class=pl-c1>=</span> <span class=pl-en>get_teams_bearer_token</span>(<span class=pl-c1>TENANT_ID</span>, <span class=pl-c1>CLIENT_ID</span>, <span class=pl-c1>CLIENT_SECRET</span>)</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/1bff25ecc8dfcc1e264da0705cfc1d0b/raw/ca0845ef78e790cab56d3ba6cabd591cb29be42a/lambda_handler1.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/1bff25ecc8dfcc1e264da0705cfc1d0b#file-lambda_handler1-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          lambda_handler1.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-110350e56c50.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-110350e56c50.css"><div id="gist139607894" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-lambda_handler1-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="8" data-paste-markdown-skip="" data-tagsearch-path="lambda_handler1.py">
        <tbody><tr>
          <td id="file-lambda_handler1-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-lambda_handler1-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-en">lambda_handler</span>(<span class="pl-s1">event</span>, <span class="pl-s1">context</span>):</td>
        </tr>
        <tr>
          <td id="file-lambda_handler1-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-lambda_handler1-py-LC2" class="blob-code blob-code-inner js-file-line">    <span class="pl-c">#... </span></td>
        </tr>
        <tr>
          <td id="file-lambda_handler1-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-lambda_handler1-py-LC3" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Fetch secret package</span></td>
        </tr>
        <tr>
          <td id="file-lambda_handler1-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-lambda_handler1-py-LC4" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">secrets</span> <span class="pl-c1">=</span> <span class="pl-en">get_secret_ssm_layer</span>(<span class="pl-s1">bot_secret_name</span>)</td>
        </tr>
        <tr>
          <td id="file-lambda_handler1-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-lambda_handler1-py-LC5" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-lambda_handler1-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-lambda_handler1-py-LC6" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Disambiguate secrets</span></td>
        </tr>
        <tr>
          <td id="file-lambda_handler1-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-lambda_handler1-py-LC7" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">secrets_json</span> <span class="pl-c1">=</span> <span class="pl-s1">json</span>.<span class="pl-c1">loads</span>(<span class="pl-s1">secrets</span>)</td>
        </tr>
        <tr>
          <td id="file-lambda_handler1-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-lambda_handler1-py-LC8" class="blob-code blob-code-inner js-file-line">    <span class="pl-c1">TENANT_ID</span> <span class="pl-c1">=</span> <span class="pl-s1">secrets_json</span>[<span class="pl-s">"TENANT_ID"</span>]</td>
        </tr>
        <tr>
          <td id="file-lambda_handler1-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-lambda_handler1-py-LC9" class="blob-code blob-code-inner js-file-line">    <span class="pl-c1">CLIENT_ID</span> <span class="pl-c1">=</span> <span class="pl-s1">secrets_json</span>[<span class="pl-s">"CLIENT_ID"</span>]</td>
        </tr>
        <tr>
          <td id="file-lambda_handler1-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-lambda_handler1-py-LC10" class="blob-code blob-code-inner js-file-line">    <span class="pl-c1">CLIENT_SECRET</span> <span class="pl-c1">=</span> <span class="pl-s1">secrets_json</span>[<span class="pl-s">"CLIENT_SECRET"</span>]</td>
        </tr>
        <tr>
          <td id="file-lambda_handler1-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-lambda_handler1-py-LC11" class="blob-code blob-code-inner js-file-line">    </td>
        </tr>
        <tr>
          <td id="file-lambda_handler1-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-lambda_handler1-py-LC12" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Get bearer token for the bot to use to post messages</span></td>
        </tr>
        <tr>
          <td id="file-lambda_handler1-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-lambda_handler1-py-LC13" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">bot_bearer_token</span> <span class="pl-c1">=</span> <span class="pl-en">get_teams_bearer_token</span>(<span class="pl-c1">TENANT_ID</span>, <span class="pl-c1">CLIENT_ID</span>, <span class="pl-c1">CLIENT_SECRET</span>)</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/1bff25ecc8dfcc1e264da0705cfc1d0b/raw/ca0845ef78e790cab56d3ba6cabd591cb29be42a/lambda_handler1.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/1bff25ecc8dfcc1e264da0705cfc1d0b#file-lambda_handler1-py" class="Link--inTextBlock">
          lambda_handler1.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>This function helps us get a token to use to post responses to the user when we&#8217;re ready to. We do this immediately so we can post error messages back if anything breaks. </p><p>On line 4, we set the token_url as a static string - this is required for all BotFramework compatible bot implementations. </p><p>Then on line 7 we assign the scope - the list of permissions we&#8217;re requesting. This bot only has the permissions required, so we don&#8217;t care if we get ALL of them. </p><blockquote><p>Pro tip: If the bot is assigned broad/admin permissions, and you only want this bot to get limited permissions, you can prune permissions back here by what you request. </p></blockquote><p>On line 10 we build our payload, and on line 18 we request the token. </p><p>On line 19, we raise an error if we get anything other than an http/200, and if we pass this check, on line 22, we extract the &#8220;access_token&#8221; from the response and return it on line 24. This helps validate that we are the bot, and we have a right to send responses AS the bot. </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist139607943\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-get_teams_bearer_token-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;get_teams_bearer_token.py content, created by KyMidd on 12:44AM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;8\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;get_teams_bearer_token.py\&quot;>\n        <tr>\n          <td id=\&quot;file-get_teams_bearer_token-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-get_teams_bearer_token-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-en>get_teams_bearer_token</span>(<span class=pl-c1>TENANT_ID</span>, <span class=pl-c1>CLIENT_ID</span>, <span class=pl-c1>CLIENT_SECRET</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_bearer_token-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-get_teams_bearer_token-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_bearer_token-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-get_teams_bearer_token-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Token endpoint for Azure AD - multi tenant</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_bearer_token-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-get_teams_bearer_token-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>token_url</span> <span class=pl-c1>=</span> <span class=pl-s>f&amp;quot;https://login.microsoftonline.com/botframework.com/oauth2/v2.0/token&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_bearer_token-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-get_teams_bearer_token-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_bearer_token-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-get_teams_bearer_token-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Bot Framework requires this scope</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_bearer_token-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-get_teams_bearer_token-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>scope</span> <span class=pl-c1>=</span> <span class=pl-s>&amp;quot;https://api.botframework.com/.default&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_bearer_token-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-get_teams_bearer_token-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_bearer_token-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-get_teams_bearer_token-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Build the request</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_bearer_token-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-get_teams_bearer_token-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>payload</span> <span class=pl-c1>=</span> {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_bearer_token-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-get_teams_bearer_token-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s>&amp;quot;grant_type&amp;quot;</span>: <span class=pl-s>&amp;quot;client_credentials&amp;quot;</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_bearer_token-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-get_teams_bearer_token-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s>&amp;quot;client_id&amp;quot;</span>: <span class=pl-c1>CLIENT_ID</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_bearer_token-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-get_teams_bearer_token-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s>&amp;quot;client_secret&amp;quot;</span>: <span class=pl-c1>CLIENT_SECRET</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_bearer_token-py-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-get_teams_bearer_token-py-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s>&amp;quot;scope&amp;quot;</span>: <span class=pl-s1>scope</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_bearer_token-py-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-get_teams_bearer_token-py-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    }</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_bearer_token-py-L16\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;16\&quot;></td>\n          <td id=\&quot;file-get_teams_bearer_token-py-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_bearer_token-py-L17\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;17\&quot;></td>\n          <td id=\&quot;file-get_teams_bearer_token-py-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Request the token</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_bearer_token-py-L18\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;18\&quot;></td>\n          <td id=\&quot;file-get_teams_bearer_token-py-LC18\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>response</span> <span class=pl-c1>=</span> <span class=pl-s1>requests</span>.<span class=pl-c1>post</span>(<span class=pl-s1>token_url</span>, <span class=pl-s1>data</span><span class=pl-c1>=</span><span class=pl-s1>payload</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_bearer_token-py-L19\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;19\&quot;></td>\n          <td id=\&quot;file-get_teams_bearer_token-py-LC19\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>response</span>.<span class=pl-c1>raise_for_status</span>()  <span class=pl-c># This will throw an error if the request fails</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_bearer_token-py-L20\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;20\&quot;></td>\n          <td id=\&quot;file-get_teams_bearer_token-py-LC20\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_bearer_token-py-L21\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;21\&quot;></td>\n          <td id=\&quot;file-get_teams_bearer_token-py-LC21\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Extract the token</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_bearer_token-py-L22\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;22\&quot;></td>\n          <td id=\&quot;file-get_teams_bearer_token-py-LC22\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>bearer_token</span> <span class=pl-c1>=</span> <span class=pl-s1>response</span>.<span class=pl-c1>json</span>()[<span class=pl-s>&amp;quot;access_token&amp;quot;</span>]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_bearer_token-py-L23\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;23\&quot;></td>\n          <td id=\&quot;file-get_teams_bearer_token-py-LC23\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_bearer_token-py-L24\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;24\&quot;></td>\n          <td id=\&quot;file-get_teams_bearer_token-py-LC24\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>return</span> <span class=pl-s1>bearer_token</span></td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/c61f33208a493997681ee8c98b97a68d/raw/232e0b12c5db3f5fadcd3fa55f669058185dc099/get_teams_bearer_token.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/c61f33208a493997681ee8c98b97a68d#file-get_teams_bearer_token-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          get_teams_bearer_token.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-110350e56c50.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-110350e56c50.css"><div id="gist139607943" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-get_teams_bearer_token-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="8" data-paste-markdown-skip="" data-tagsearch-path="get_teams_bearer_token.py">
        <tbody><tr>
          <td id="file-get_teams_bearer_token-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-get_teams_bearer_token-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-en">get_teams_bearer_token</span>(<span class="pl-c1">TENANT_ID</span>, <span class="pl-c1">CLIENT_ID</span>, <span class="pl-c1">CLIENT_SECRET</span>):</td>
        </tr>
        <tr>
          <td id="file-get_teams_bearer_token-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-get_teams_bearer_token-py-LC2" class="blob-code blob-code-inner js-file-line">  </td>
        </tr>
        <tr>
          <td id="file-get_teams_bearer_token-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-get_teams_bearer_token-py-LC3" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Token endpoint for Azure AD - multi tenant</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_bearer_token-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-get_teams_bearer_token-py-LC4" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">token_url</span> <span class="pl-c1">=</span> <span class="pl-s">f"https://login.microsoftonline.com/botframework.com/oauth2/v2.0/token"</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_bearer_token-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-get_teams_bearer_token-py-LC5" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-get_teams_bearer_token-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-get_teams_bearer_token-py-LC6" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Bot Framework requires this scope</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_bearer_token-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-get_teams_bearer_token-py-LC7" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">scope</span> <span class="pl-c1">=</span> <span class="pl-s">"https://api.botframework.com/.default"</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_bearer_token-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-get_teams_bearer_token-py-LC8" class="blob-code blob-code-inner js-file-line">    </td>
        </tr>
        <tr>
          <td id="file-get_teams_bearer_token-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-get_teams_bearer_token-py-LC9" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Build the request</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_bearer_token-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-get_teams_bearer_token-py-LC10" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">payload</span> <span class="pl-c1">=</span> {</td>
        </tr>
        <tr>
          <td id="file-get_teams_bearer_token-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-get_teams_bearer_token-py-LC11" class="blob-code blob-code-inner js-file-line">        <span class="pl-s">"grant_type"</span>: <span class="pl-s">"client_credentials"</span>,</td>
        </tr>
        <tr>
          <td id="file-get_teams_bearer_token-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-get_teams_bearer_token-py-LC12" class="blob-code blob-code-inner js-file-line">        <span class="pl-s">"client_id"</span>: <span class="pl-c1">CLIENT_ID</span>,</td>
        </tr>
        <tr>
          <td id="file-get_teams_bearer_token-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-get_teams_bearer_token-py-LC13" class="blob-code blob-code-inner js-file-line">        <span class="pl-s">"client_secret"</span>: <span class="pl-c1">CLIENT_SECRET</span>,</td>
        </tr>
        <tr>
          <td id="file-get_teams_bearer_token-py-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-get_teams_bearer_token-py-LC14" class="blob-code blob-code-inner js-file-line">        <span class="pl-s">"scope"</span>: <span class="pl-s1">scope</span>,</td>
        </tr>
        <tr>
          <td id="file-get_teams_bearer_token-py-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-get_teams_bearer_token-py-LC15" class="blob-code blob-code-inner js-file-line">    }</td>
        </tr>
        <tr>
          <td id="file-get_teams_bearer_token-py-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-get_teams_bearer_token-py-LC16" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-get_teams_bearer_token-py-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-get_teams_bearer_token-py-LC17" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Request the token</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_bearer_token-py-L18" class="blob-num js-line-number js-blob-rnum" data-line-number="18"></td>
          <td id="file-get_teams_bearer_token-py-LC18" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">response</span> <span class="pl-c1">=</span> <span class="pl-s1">requests</span>.<span class="pl-c1">post</span>(<span class="pl-s1">token_url</span>, <span class="pl-s1">data</span><span class="pl-c1">=</span><span class="pl-s1">payload</span>)</td>
        </tr>
        <tr>
          <td id="file-get_teams_bearer_token-py-L19" class="blob-num js-line-number js-blob-rnum" data-line-number="19"></td>
          <td id="file-get_teams_bearer_token-py-LC19" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">response</span>.<span class="pl-c1">raise_for_status</span>()  <span class="pl-c"># This will throw an error if the request fails</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_bearer_token-py-L20" class="blob-num js-line-number js-blob-rnum" data-line-number="20"></td>
          <td id="file-get_teams_bearer_token-py-LC20" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-get_teams_bearer_token-py-L21" class="blob-num js-line-number js-blob-rnum" data-line-number="21"></td>
          <td id="file-get_teams_bearer_token-py-LC21" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Extract the token</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_bearer_token-py-L22" class="blob-num js-line-number js-blob-rnum" data-line-number="22"></td>
          <td id="file-get_teams_bearer_token-py-LC22" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">bearer_token</span> <span class="pl-c1">=</span> <span class="pl-s1">response</span>.<span class="pl-c1">json</span>()[<span class="pl-s">"access_token"</span>]</td>
        </tr>
        <tr>
          <td id="file-get_teams_bearer_token-py-L23" class="blob-num js-line-number js-blob-rnum" data-line-number="23"></td>
          <td id="file-get_teams_bearer_token-py-LC23" class="blob-code blob-code-inner js-file-line">    </td>
        </tr>
        <tr>
          <td id="file-get_teams_bearer_token-py-L24" class="blob-num js-line-number js-blob-rnum" data-line-number="24"></td>
          <td id="file-get_teams_bearer_token-py-LC24" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">return</span> <span class="pl-s1">bearer_token</span></td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/c61f33208a493997681ee8c98b97a68d/raw/232e0b12c5db3f5fadcd3fa55f669058185dc099/get_teams_bearer_token.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/c61f33208a493997681ee8c98b97a68d#file-get_teams_bearer_token-py" class="Link--inTextBlock">
          get_teams_bearer_token.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><h1>Extracting the Auth Token</h1><p>Before we do anything, we need the auth token so we can operate on behalf of the user. Remember, the &#8220;Receiver&#8221; lambda either had this cached or asked the user to go approve it through the SSO Card we sent. The Worker lambda (that does all this AI stuff) won&#8217;t be triggered until the auth token is valid and received. </p><p>Remember, we packed the auth token in the &#8220;event&#8221; that is passed from the Receiver (think Receptionist), as an encrypted, base64&#8217;d string. </p><p>Lets fetch the token blob on line 7 from the event payload, and then send it to the decrypt_user_auth_token() function on line 8. Lets follow that thread. </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist139607995\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-lambda_handler2-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;lambda_handler2.py content, created by KyMidd on 12:48AM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;8\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;lambda_handler2.py\&quot;>\n        <tr>\n          <td id=\&quot;file-lambda_handler2-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-lambda_handler2-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-en>lambda_handler</span>(<span class=pl-s1>event</span>, <span class=pl-s1>context</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler2-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-lambda_handler2-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c>#...</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler2-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-lambda_handler2-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Get bearer token for the bot to use to post messages</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler2-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-lambda_handler2-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>bot_bearer_token</span> <span class=pl-c1>=</span> <span class=pl-en>get_teams_bearer_token</span>(<span class=pl-c1>TENANT_ID</span>, <span class=pl-c1>CLIENT_ID</span>, <span class=pl-c1>CLIENT_SECRET</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler2-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-lambda_handler2-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler2-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-lambda_handler2-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Extract auth token from event</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler2-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-lambda_handler2-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>user_graph_auth_token</span> <span class=pl-c1>=</span> <span class=pl-s1>event</span>.<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;token&amp;quot;</span>, <span class=pl-s>&amp;quot;&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler2-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-lambda_handler2-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>user_graph_auth_token</span> <span class=pl-c1>=</span> <span class=pl-en>decrypt_user_auth_token</span>(<span class=pl-s1>user_graph_auth_token</span>)</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/9a32a43ed59ab513e52d14b910de6c56/raw/f8be480b875958bb713b3ce4f06e8321e6f0fc93/lambda_handler2.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/9a32a43ed59ab513e52d14b910de6c56#file-lambda_handler2-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          lambda_handler2.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-110350e56c50.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-110350e56c50.css"><div id="gist139607995" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-lambda_handler2-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="8" data-paste-markdown-skip="" data-tagsearch-path="lambda_handler2.py">
        <tbody><tr>
          <td id="file-lambda_handler2-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-lambda_handler2-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-en">lambda_handler</span>(<span class="pl-s1">event</span>, <span class="pl-s1">context</span>):</td>
        </tr>
        <tr>
          <td id="file-lambda_handler2-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-lambda_handler2-py-LC2" class="blob-code blob-code-inner js-file-line">    <span class="pl-c">#...</span></td>
        </tr>
        <tr>
          <td id="file-lambda_handler2-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-lambda_handler2-py-LC3" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Get bearer token for the bot to use to post messages</span></td>
        </tr>
        <tr>
          <td id="file-lambda_handler2-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-lambda_handler2-py-LC4" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">bot_bearer_token</span> <span class="pl-c1">=</span> <span class="pl-en">get_teams_bearer_token</span>(<span class="pl-c1">TENANT_ID</span>, <span class="pl-c1">CLIENT_ID</span>, <span class="pl-c1">CLIENT_SECRET</span>)</td>
        </tr>
        <tr>
          <td id="file-lambda_handler2-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-lambda_handler2-py-LC5" class="blob-code blob-code-inner js-file-line">        </td>
        </tr>
        <tr>
          <td id="file-lambda_handler2-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-lambda_handler2-py-LC6" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Extract auth token from event</span></td>
        </tr>
        <tr>
          <td id="file-lambda_handler2-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-lambda_handler2-py-LC7" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">user_graph_auth_token</span> <span class="pl-c1">=</span> <span class="pl-s1">event</span>.<span class="pl-c1">get</span>(<span class="pl-s">"token"</span>, <span class="pl-s">""</span>)</td>
        </tr>
        <tr>
          <td id="file-lambda_handler2-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-lambda_handler2-py-LC8" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">user_graph_auth_token</span> <span class="pl-c1">=</span> <span class="pl-en">decrypt_user_auth_token</span>(<span class="pl-s1">user_graph_auth_token</span>)</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/9a32a43ed59ab513e52d14b910de6c56/raw/f8be480b875958bb713b3ce4f06e8321e6f0fc93/lambda_handler2.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/9a32a43ed59ab513e52d14b910de6c56#file-lambda_handler2-py" class="Link--inTextBlock">
          lambda_handler2.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>The token is base64&#8217;d after being encrypted, so lets decode it on line 4. </p><p>Then we register a KMS client on line 7, and read the KMS CMK alias that we passed in from terraform as an environmental variable on line 10. </p><p>Then we pass the token (no longer encoded as base64, but still an encrypted blob) and the key alias to the kms.decrypt() function. </p><p>On line 19, we identify the plaintext response from the KMS call, and decode it as a utf-8 string. </p><p>PHEW, that was a lot to get the token. But it does very much help us not pass plaintext access tokens from users, and to not store them in dynamo unencrypted. </p><p>On line 22, we send the decrypted access token back. </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist139608010\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-decrypt_user_auth_token-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;decrypt_user_auth_token.py content, created by KyMidd on 12:49AM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;8\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;decrypt_user_auth_token.py\&quot;>\n        <tr>\n          <td id=\&quot;file-decrypt_user_auth_token-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-decrypt_user_auth_token-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-en>decrypt_user_auth_token</span>(<span class=pl-s1>encrypted_token</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-decrypt_user_auth_token-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-decrypt_user_auth_token-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-decrypt_user_auth_token-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-decrypt_user_auth_token-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Decode the base64</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-decrypt_user_auth_token-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-decrypt_user_auth_token-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>encrypted_token</span> <span class=pl-c1>=</span> <span class=pl-s1>base64</span>.<span class=pl-c1>b64decode</span>(<span class=pl-s1>encrypted_token</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-decrypt_user_auth_token-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-decrypt_user_auth_token-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-decrypt_user_auth_token-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-decrypt_user_auth_token-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Initialize KMS client</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-decrypt_user_auth_token-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-decrypt_user_auth_token-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>kms</span> <span class=pl-c1>=</span> <span class=pl-s1>boto3</span>.<span class=pl-c1>client</span>(<span class=pl-s>&amp;#39;kms&amp;#39;</span>, <span class=pl-s1>region_name</span><span class=pl-c1>=</span><span class=pl-s>&amp;#39;us-east-1&amp;#39;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-decrypt_user_auth_token-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-decrypt_user_auth_token-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-decrypt_user_auth_token-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-decrypt_user_auth_token-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Read the CMK key alias from the environment variable</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-decrypt_user_auth_token-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-decrypt_user_auth_token-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>cmk_key_alias</span> <span class=pl-c1>=</span> <span class=pl-s1>os</span>.<span class=pl-c1>environ</span>.<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;CMK_ALIAS&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-decrypt_user_auth_token-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-decrypt_user_auth_token-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-decrypt_user_auth_token-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-decrypt_user_auth_token-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Decrypt the token</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-decrypt_user_auth_token-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-decrypt_user_auth_token-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>response</span> <span class=pl-c1>=</span> <span class=pl-s1>kms</span>.<span class=pl-c1>decrypt</span>(</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-decrypt_user_auth_token-py-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-decrypt_user_auth_token-py-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-v>KeyId</span><span class=pl-c1>=</span><span class=pl-s1>cmk_key_alias</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-decrypt_user_auth_token-py-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-decrypt_user_auth_token-py-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-v>CiphertextBlob</span><span class=pl-c1>=</span><span class=pl-s1>encrypted_token</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-decrypt_user_auth_token-py-L16\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;16\&quot;></td>\n          <td id=\&quot;file-decrypt_user_auth_token-py-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    )</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-decrypt_user_auth_token-py-L17\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;17\&quot;></td>\n          <td id=\&quot;file-decrypt_user_auth_token-py-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-decrypt_user_auth_token-py-L18\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;18\&quot;></td>\n          <td id=\&quot;file-decrypt_user_auth_token-py-LC18\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Store accessToken in variable</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-decrypt_user_auth_token-py-L19\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;19\&quot;></td>\n          <td id=\&quot;file-decrypt_user_auth_token-py-LC19\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>accessToken</span> <span class=pl-c1>=</span> <span class=pl-s1>response</span>[<span class=pl-s>&amp;quot;Plaintext&amp;quot;</span>].<span class=pl-c1>decode</span>(<span class=pl-s>&amp;quot;utf-8&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-decrypt_user_auth_token-py-L20\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;20\&quot;></td>\n          <td id=\&quot;file-decrypt_user_auth_token-py-LC20\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-decrypt_user_auth_token-py-L21\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;21\&quot;></td>\n          <td id=\&quot;file-decrypt_user_auth_token-py-LC21\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Send the accessToken back</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-decrypt_user_auth_token-py-L22\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;22\&quot;></td>\n          <td id=\&quot;file-decrypt_user_auth_token-py-LC22\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>return</span> <span class=pl-s1>accessToken</span></td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/e7f98222fe09ea7c8bdf162fa0f1be1f/raw/8455874f88cb5666e7edc67c90c2781e896925d2/decrypt_user_auth_token.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/e7f98222fe09ea7c8bdf162fa0f1be1f#file-decrypt_user_auth_token-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          decrypt_user_auth_token.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-110350e56c50.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-110350e56c50.css"><div id="gist139608010" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-decrypt_user_auth_token-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="8" data-paste-markdown-skip="" data-tagsearch-path="decrypt_user_auth_token.py">
        <tbody><tr>
          <td id="file-decrypt_user_auth_token-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-decrypt_user_auth_token-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-en">decrypt_user_auth_token</span>(<span class="pl-s1">encrypted_token</span>):</td>
        </tr>
        <tr>
          <td id="file-decrypt_user_auth_token-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-decrypt_user_auth_token-py-LC2" class="blob-code blob-code-inner js-file-line">    </td>
        </tr>
        <tr>
          <td id="file-decrypt_user_auth_token-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-decrypt_user_auth_token-py-LC3" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Decode the base64</span></td>
        </tr>
        <tr>
          <td id="file-decrypt_user_auth_token-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-decrypt_user_auth_token-py-LC4" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">encrypted_token</span> <span class="pl-c1">=</span> <span class="pl-s1">base64</span>.<span class="pl-c1">b64decode</span>(<span class="pl-s1">encrypted_token</span>)</td>
        </tr>
        <tr>
          <td id="file-decrypt_user_auth_token-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-decrypt_user_auth_token-py-LC5" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-decrypt_user_auth_token-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-decrypt_user_auth_token-py-LC6" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Initialize KMS client</span></td>
        </tr>
        <tr>
          <td id="file-decrypt_user_auth_token-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-decrypt_user_auth_token-py-LC7" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">kms</span> <span class="pl-c1">=</span> <span class="pl-s1">boto3</span>.<span class="pl-c1">client</span>(<span class="pl-s">'kms'</span>, <span class="pl-s1">region_name</span><span class="pl-c1">=</span><span class="pl-s">'us-east-1'</span>)</td>
        </tr>
        <tr>
          <td id="file-decrypt_user_auth_token-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-decrypt_user_auth_token-py-LC8" class="blob-code blob-code-inner js-file-line">    </td>
        </tr>
        <tr>
          <td id="file-decrypt_user_auth_token-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-decrypt_user_auth_token-py-LC9" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Read the CMK key alias from the environment variable</span></td>
        </tr>
        <tr>
          <td id="file-decrypt_user_auth_token-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-decrypt_user_auth_token-py-LC10" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">cmk_key_alias</span> <span class="pl-c1">=</span> <span class="pl-s1">os</span>.<span class="pl-c1">environ</span>.<span class="pl-c1">get</span>(<span class="pl-s">"CMK_ALIAS"</span>)</td>
        </tr>
        <tr>
          <td id="file-decrypt_user_auth_token-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-decrypt_user_auth_token-py-LC11" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-decrypt_user_auth_token-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-decrypt_user_auth_token-py-LC12" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Decrypt the token</span></td>
        </tr>
        <tr>
          <td id="file-decrypt_user_auth_token-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-decrypt_user_auth_token-py-LC13" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">response</span> <span class="pl-c1">=</span> <span class="pl-s1">kms</span>.<span class="pl-c1">decrypt</span>(</td>
        </tr>
        <tr>
          <td id="file-decrypt_user_auth_token-py-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-decrypt_user_auth_token-py-LC14" class="blob-code blob-code-inner js-file-line">        <span class="pl-v">KeyId</span><span class="pl-c1">=</span><span class="pl-s1">cmk_key_alias</span>,</td>
        </tr>
        <tr>
          <td id="file-decrypt_user_auth_token-py-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-decrypt_user_auth_token-py-LC15" class="blob-code blob-code-inner js-file-line">        <span class="pl-v">CiphertextBlob</span><span class="pl-c1">=</span><span class="pl-s1">encrypted_token</span></td>
        </tr>
        <tr>
          <td id="file-decrypt_user_auth_token-py-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-decrypt_user_auth_token-py-LC16" class="blob-code blob-code-inner js-file-line">    )</td>
        </tr>
        <tr>
          <td id="file-decrypt_user_auth_token-py-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-decrypt_user_auth_token-py-LC17" class="blob-code blob-code-inner js-file-line">    </td>
        </tr>
        <tr>
          <td id="file-decrypt_user_auth_token-py-L18" class="blob-num js-line-number js-blob-rnum" data-line-number="18"></td>
          <td id="file-decrypt_user_auth_token-py-LC18" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Store accessToken in variable</span></td>
        </tr>
        <tr>
          <td id="file-decrypt_user_auth_token-py-L19" class="blob-num js-line-number js-blob-rnum" data-line-number="19"></td>
          <td id="file-decrypt_user_auth_token-py-LC19" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">accessToken</span> <span class="pl-c1">=</span> <span class="pl-s1">response</span>[<span class="pl-s">"Plaintext"</span>].<span class="pl-c1">decode</span>(<span class="pl-s">"utf-8"</span>)</td>
        </tr>
        <tr>
          <td id="file-decrypt_user_auth_token-py-L20" class="blob-num js-line-number js-blob-rnum" data-line-number="20"></td>
          <td id="file-decrypt_user_auth_token-py-LC20" class="blob-code blob-code-inner js-file-line">    </td>
        </tr>
        <tr>
          <td id="file-decrypt_user_auth_token-py-L21" class="blob-num js-line-number js-blob-rnum" data-line-number="21"></td>
          <td id="file-decrypt_user_auth_token-py-LC21" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Send the accessToken back</span></td>
        </tr>
        <tr>
          <td id="file-decrypt_user_auth_token-py-L22" class="blob-num js-line-number js-blob-rnum" data-line-number="22"></td>
          <td id="file-decrypt_user_auth_token-py-LC22" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">return</span> <span class="pl-s1">accessToken</span></td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/e7f98222fe09ea7c8bdf162fa0f1be1f/raw/8455874f88cb5666e7edc67c90c2781e896925d2/decrypt_user_auth_token.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/e7f98222fe09ea7c8bdf162fa0f1be1f#file-decrypt_user_auth_token-py" class="Link--inTextBlock">
          decrypt_user_auth_token.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>With that token passed back, lets head back to our main function. </p><p>Next up, we register the bedrock client, line 4, in the proper region. This is the client which lets us talk to bedrock for AI stuff. </p><p>Then we pass all the token and stuff to the message_handler() function in a try() block. If anything goes wrong in the bedrock AI call, we should be able to send the error right back to the user with lines 9 - 18. </p><p>Lets follow the message_handler() logic. </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist139608070\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-lambda_handler3-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;lambda_handler3.py content, created by KyMidd on 12:54AM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;8\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;lambda_handler3.py\&quot;>\n        <tr>\n          <td id=\&quot;file-lambda_handler3-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-lambda_handler3-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-en>lambda_handler</span>(<span class=pl-s1>event</span>, <span class=pl-s1>context</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler3-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-lambda_handler3-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c>#...</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler3-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-lambda_handler3-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Build bedrock client</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler3-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-lambda_handler3-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>bedrock_client</span> <span class=pl-c1>=</span> <span class=pl-en>create_bedrock_client</span>(<span class=pl-s1>model_region_name</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler3-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-lambda_handler3-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler3-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-lambda_handler3-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Build conversation, knowledge base, rerank, AI request, respond to Teams</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler3-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-lambda_handler3-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>try</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler3-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-lambda_handler3-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-en>message_handler</span>(<span class=pl-s1>event_body</span>, <span class=pl-s1>bot_bearer_token</span>, <span class=pl-s1>user_graph_auth_token</span>, <span class=pl-s1>bedrock_client</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler3-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-lambda_handler3-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>except</span> <span class=pl-v>Exception</span> <span class=pl-k>as</span> <span class=pl-s1>error</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler3-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-lambda_handler3-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># If the request fails, print the error</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler3-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-lambda_handler3-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-en>print</span>(<span class=pl-s>f&amp;quot;&#128640; Error making request to Bedrock: <span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>error</span><span class=pl-kos>}</span></span>&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler3-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-lambda_handler3-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler3-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-lambda_handler3-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># Return error as response</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler3-py-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-lambda_handler3-py-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-en>respond_to_teams</span>(</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler3-py-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-lambda_handler3-py-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>bot_bearer_token</span>, </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler3-py-L16\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;16\&quot;></td>\n          <td id=\&quot;file-lambda_handler3-py-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>event_body</span>, </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler3-py-L17\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;17\&quot;></td>\n          <td id=\&quot;file-lambda_handler3-py-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s>f&amp;quot;&#128532; Error making request to Bedrock: &amp;quot;</span> <span class=pl-c1>+</span> <span class=pl-en>str</span>(<span class=pl-s1>error</span>),</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler3-py-L18\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;18\&quot;></td>\n          <td id=\&quot;file-lambda_handler3-py-LC18\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        )</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/1c5d2ef1f8dddfc5e70aa8ea2e1700b2/raw/659b26e3f903d413a8a94c1c68239880c760f1e3/lambda_handler3.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/1c5d2ef1f8dddfc5e70aa8ea2e1700b2#file-lambda_handler3-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          lambda_handler3.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-110350e56c50.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-110350e56c50.css"><div id="gist139608070" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-lambda_handler3-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="8" data-paste-markdown-skip="" data-tagsearch-path="lambda_handler3.py">
        <tbody><tr>
          <td id="file-lambda_handler3-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-lambda_handler3-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-en">lambda_handler</span>(<span class="pl-s1">event</span>, <span class="pl-s1">context</span>):</td>
        </tr>
        <tr>
          <td id="file-lambda_handler3-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-lambda_handler3-py-LC2" class="blob-code blob-code-inner js-file-line">    <span class="pl-c">#...</span></td>
        </tr>
        <tr>
          <td id="file-lambda_handler3-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-lambda_handler3-py-LC3" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Build bedrock client</span></td>
        </tr>
        <tr>
          <td id="file-lambda_handler3-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-lambda_handler3-py-LC4" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">bedrock_client</span> <span class="pl-c1">=</span> <span class="pl-en">create_bedrock_client</span>(<span class="pl-s1">model_region_name</span>)</td>
        </tr>
        <tr>
          <td id="file-lambda_handler3-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-lambda_handler3-py-LC5" class="blob-code blob-code-inner js-file-line">    </td>
        </tr>
        <tr>
          <td id="file-lambda_handler3-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-lambda_handler3-py-LC6" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Build conversation, knowledge base, rerank, AI request, respond to Teams</span></td>
        </tr>
        <tr>
          <td id="file-lambda_handler3-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-lambda_handler3-py-LC7" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">try</span>:</td>
        </tr>
        <tr>
          <td id="file-lambda_handler3-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-lambda_handler3-py-LC8" class="blob-code blob-code-inner js-file-line">        <span class="pl-en">message_handler</span>(<span class="pl-s1">event_body</span>, <span class="pl-s1">bot_bearer_token</span>, <span class="pl-s1">user_graph_auth_token</span>, <span class="pl-s1">bedrock_client</span>)</td>
        </tr>
        <tr>
          <td id="file-lambda_handler3-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-lambda_handler3-py-LC9" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">except</span> <span class="pl-v">Exception</span> <span class="pl-k">as</span> <span class="pl-s1">error</span>:</td>
        </tr>
        <tr>
          <td id="file-lambda_handler3-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-lambda_handler3-py-LC10" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># If the request fails, print the error</span></td>
        </tr>
        <tr>
          <td id="file-lambda_handler3-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-lambda_handler3-py-LC11" class="blob-code blob-code-inner js-file-line">        <span class="pl-en">print</span>(<span class="pl-s">f"&#128640; Error making request to Bedrock: <span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">error</span><span class="pl-kos">}</span></span>"</span>)</td>
        </tr>
        <tr>
          <td id="file-lambda_handler3-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-lambda_handler3-py-LC12" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-lambda_handler3-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-lambda_handler3-py-LC13" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># Return error as response</span></td>
        </tr>
        <tr>
          <td id="file-lambda_handler3-py-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-lambda_handler3-py-LC14" class="blob-code blob-code-inner js-file-line">        <span class="pl-en">respond_to_teams</span>(</td>
        </tr>
        <tr>
          <td id="file-lambda_handler3-py-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-lambda_handler3-py-LC15" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">bot_bearer_token</span>, </td>
        </tr>
        <tr>
          <td id="file-lambda_handler3-py-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-lambda_handler3-py-LC16" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">event_body</span>, </td>
        </tr>
        <tr>
          <td id="file-lambda_handler3-py-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-lambda_handler3-py-LC17" class="blob-code blob-code-inner js-file-line">            <span class="pl-s">f"&#128532; Error making request to Bedrock: "</span> <span class="pl-c1">+</span> <span class="pl-en">str</span>(<span class="pl-s1">error</span>),</td>
        </tr>
        <tr>
          <td id="file-lambda_handler3-py-L18" class="blob-num js-line-number js-blob-rnum" data-line-number="18"></td>
          <td id="file-lambda_handler3-py-LC18" class="blob-code blob-code-inner js-file-line">        )</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/1c5d2ef1f8dddfc5e70aa8ea2e1700b2/raw/659b26e3f903d413a8a94c1c68239880c760f1e3/lambda_handler3.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/1c5d2ef1f8dddfc5e70aa8ea2e1700b2#file-lambda_handler3-py" class="Link--inTextBlock">
          lambda_handler3.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><h1>Following the Message Handler</h1><p>Now, the Teams response takes a bit longer than Slack does, something like 7-10 seconds. That&#8217;s a long time to wait for any response, you might think it&#8217;s broken! So I decided to build an immediate &#8220;working hard&#8221; message and I had fun with it. </p><p>From line 2-7 we created some messages, and then on line 12, we randomly pick one of those 4 and set it as the right-away response. </p><p>Then on line 15, we send the response to the user. It looks like this:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!gRbZ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76d5c8ec-53cc-48b2-b700-ee95c85d5fb0_1048x184.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gRbZ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76d5c8ec-53cc-48b2-b700-ee95c85d5fb0_1048x184.png 424w, https://substackcdn.com/image/fetch/$s_!gRbZ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76d5c8ec-53cc-48b2-b700-ee95c85d5fb0_1048x184.png 848w, https://substackcdn.com/image/fetch/$s_!gRbZ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76d5c8ec-53cc-48b2-b700-ee95c85d5fb0_1048x184.png 1272w, https://substackcdn.com/image/fetch/$s_!gRbZ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76d5c8ec-53cc-48b2-b700-ee95c85d5fb0_1048x184.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!gRbZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76d5c8ec-53cc-48b2-b700-ee95c85d5fb0_1048x184.png" width="1048" height="184" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/76d5c8ec-53cc-48b2-b700-ee95c85d5fb0_1048x184.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:184,&quot;width&quot;:1048,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:36118,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.letsdodevops.com/i/168320556?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76d5c8ec-53cc-48b2-b700-ee95c85d5fb0_1048x184.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!gRbZ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76d5c8ec-53cc-48b2-b700-ee95c85d5fb0_1048x184.png 424w, https://substackcdn.com/image/fetch/$s_!gRbZ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76d5c8ec-53cc-48b2-b700-ee95c85d5fb0_1048x184.png 848w, https://substackcdn.com/image/fetch/$s_!gRbZ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76d5c8ec-53cc-48b2-b700-ee95c85d5fb0_1048x184.png 1272w, https://substackcdn.com/image/fetch/$s_!gRbZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76d5c8ec-53cc-48b2-b700-ee95c85d5fb0_1048x184.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Let&#8217;s look closer at the respond_to_teams() function before we continue on. </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist139608199\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-message_handler1-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;message_handler1.py content, created by KyMidd on 01:05AM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;8\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;message_handler1.py\&quot;>\n        <tr>\n          <td id=\&quot;file-message_handler1-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-message_handler1-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c># &amp;quot;Loading&amp;quot; messages for Teams</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-message_handler1-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-message_handler1-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s1>teams_loading_responses</span> <span class=pl-c1>=</span> [</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-message_handler1-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-message_handler1-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s>&amp;quot;&#129300; Vera is reading our knowledge bases and building a response&amp;quot;</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-message_handler1-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-message_handler1-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s>&amp;quot;&#129299; Vera&amp;#39;s reading ALL the docs and will respond soon&amp;quot;</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-message_handler1-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-message_handler1-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s>&amp;quot;&#129302; Vera is reading everything and computing a response beep boop&amp;quot;</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-message_handler1-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-message_handler1-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s>&amp;quot;&#129302; Vera&amp;#39;s doing her best here, getting back to you shortly&amp;quot;</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-message_handler1-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-message_handler1-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-message_handler1-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-message_handler1-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-message_handler1-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-message_handler1-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-en>message_handler</span>(<span class=pl-s1>event_body</span>, <span class=pl-s1>bot_bearer_token</span>, <span class=pl-s1>user_graph_auth_token</span>, <span class=pl-s1>bedrock_client</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-message_handler1-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-message_handler1-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-message_handler1-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-message_handler1-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Randomly select a response</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-message_handler1-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-message_handler1-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>response</span> <span class=pl-c1>=</span> <span class=pl-s1>random</span>.<span class=pl-c1>choice</span>(<span class=pl-s1>teams_loading_responses</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-message_handler1-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-message_handler1-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-message_handler1-py-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-message_handler1-py-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># We&amp;#39;re on it</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-message_handler1-py-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-message_handler1-py-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-en>respond_to_teams</span>(</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-message_handler1-py-L16\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;16\&quot;></td>\n          <td id=\&quot;file-message_handler1-py-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>bot_bearer_token</span>, </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-message_handler1-py-L17\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;17\&quot;></td>\n          <td id=\&quot;file-message_handler1-py-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>event_body</span>, </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-message_handler1-py-L18\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;18\&quot;></td>\n          <td id=\&quot;file-message_handler1-py-LC18\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>response</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-message_handler1-py-L19\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;19\&quot;></td>\n          <td id=\&quot;file-message_handler1-py-LC19\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    )</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/3ca562a18ccdd4637a812e7dba14d225/raw/7c43021da87147aa4ffc167fe5c0e0210dcdbe6a/message_handler1.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/3ca562a18ccdd4637a812e7dba14d225#file-message_handler1-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          message_handler1.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-110350e56c50.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-110350e56c50.css"><div id="gist139608199" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-message_handler1-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="8" data-paste-markdown-skip="" data-tagsearch-path="message_handler1.py">
        <tbody><tr>
          <td id="file-message_handler1-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-message_handler1-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-c"># "Loading" messages for Teams</span></td>
        </tr>
        <tr>
          <td id="file-message_handler1-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-message_handler1-py-LC2" class="blob-code blob-code-inner js-file-line"><span class="pl-s1">teams_loading_responses</span> <span class="pl-c1">=</span> [</td>
        </tr>
        <tr>
          <td id="file-message_handler1-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-message_handler1-py-LC3" class="blob-code blob-code-inner js-file-line">    <span class="pl-s">"&#129300; Vera is reading our knowledge bases and building a response"</span>,</td>
        </tr>
        <tr>
          <td id="file-message_handler1-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-message_handler1-py-LC4" class="blob-code blob-code-inner js-file-line">    <span class="pl-s">"&#129299; Vera's reading ALL the docs and will respond soon"</span>,</td>
        </tr>
        <tr>
          <td id="file-message_handler1-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-message_handler1-py-LC5" class="blob-code blob-code-inner js-file-line">    <span class="pl-s">"&#129302; Vera is reading everything and computing a response beep boop"</span>,</td>
        </tr>
        <tr>
          <td id="file-message_handler1-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-message_handler1-py-LC6" class="blob-code blob-code-inner js-file-line">    <span class="pl-s">"&#129302; Vera's doing her best here, getting back to you shortly"</span>,</td>
        </tr>
        <tr>
          <td id="file-message_handler1-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-message_handler1-py-LC7" class="blob-code blob-code-inner js-file-line">]</td>
        </tr>
        <tr>
          <td id="file-message_handler1-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-message_handler1-py-LC8" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-message_handler1-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-message_handler1-py-LC9" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-en">message_handler</span>(<span class="pl-s1">event_body</span>, <span class="pl-s1">bot_bearer_token</span>, <span class="pl-s1">user_graph_auth_token</span>, <span class="pl-s1">bedrock_client</span>):</td>
        </tr>
        <tr>
          <td id="file-message_handler1-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-message_handler1-py-LC10" class="blob-code blob-code-inner js-file-line">    </td>
        </tr>
        <tr>
          <td id="file-message_handler1-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-message_handler1-py-LC11" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Randomly select a response</span></td>
        </tr>
        <tr>
          <td id="file-message_handler1-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-message_handler1-py-LC12" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">response</span> <span class="pl-c1">=</span> <span class="pl-s1">random</span>.<span class="pl-c1">choice</span>(<span class="pl-s1">teams_loading_responses</span>)</td>
        </tr>
        <tr>
          <td id="file-message_handler1-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-message_handler1-py-LC13" class="blob-code blob-code-inner js-file-line">    </td>
        </tr>
        <tr>
          <td id="file-message_handler1-py-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-message_handler1-py-LC14" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># We're on it</span></td>
        </tr>
        <tr>
          <td id="file-message_handler1-py-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-message_handler1-py-LC15" class="blob-code blob-code-inner js-file-line">    <span class="pl-en">respond_to_teams</span>(</td>
        </tr>
        <tr>
          <td id="file-message_handler1-py-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-message_handler1-py-LC16" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">bot_bearer_token</span>, </td>
        </tr>
        <tr>
          <td id="file-message_handler1-py-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-message_handler1-py-LC17" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">event_body</span>, </td>
        </tr>
        <tr>
          <td id="file-message_handler1-py-L18" class="blob-num js-line-number js-blob-rnum" data-line-number="18"></td>
          <td id="file-message_handler1-py-LC18" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">response</span>,</td>
        </tr>
        <tr>
          <td id="file-message_handler1-py-L19" class="blob-num js-line-number js-blob-rnum" data-line-number="19"></td>
          <td id="file-message_handler1-py-LC19" class="blob-code blob-code-inner js-file-line">    )</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/3ca562a18ccdd4637a812e7dba14d225/raw/7c43021da87147aa4ffc167fe5c0e0210dcdbe6a/message_handler1.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/3ca562a18ccdd4637a812e7dba14d225#file-message_handler1-py" class="Link--inTextBlock">
          message_handler1.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><h1>The Arduous Journey of Building Conversation Context in Teams</h1><p>There are a few ways to send responses to Teams - the BotKit has a few smooth ways, but I wanted to learn what I&#8217;m able, so I built it myself with the requests() library for pure http. </p><p>So we start with building the headers, line 4 of a Bearer token (remember the Bot token that authorizes us to send messages as the bot?). </p><p>Then we build the payload, which is the text response we&#8217;ll send, line 10-13. </p><p>Then we extract the serviceUrl (the URL we can POST a response to in order to respond, this comes in with the event_body), line 16, and the conversation ID, also in the event body, line 17. </p><p>Then we compile the URL we&#8217;ll post to, on line 21. Why do we have to construct a new URL to post a response, instead of just using the service_url that Teams sends us? I don&#8217;t know, maybe Teams engineers want to punish us <strong>&#175;\_(&#12484;)_/&#175;</strong></p><p>On line 20, we send the response. </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist139608230\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-respond_to_teams-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;respond_to_teams.py content, created by KyMidd on 01:08AM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;8\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;respond_to_teams.py\&quot;>\n        <tr>\n          <td id=\&quot;file-respond_to_teams-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-respond_to_teams-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-en>respond_to_teams</span>(<span class=pl-s1>bot_bearer_token</span>, <span class=pl-s1>event_body</span>, <span class=pl-s1>message</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-respond_to_teams-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-respond_to_teams-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-respond_to_teams-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-respond_to_teams-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Build auth headers</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-respond_to_teams-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-respond_to_teams-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>headers</span> <span class=pl-c1>=</span> {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-respond_to_teams-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-respond_to_teams-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s>&amp;quot;Authorization&amp;quot;</span>: <span class=pl-s>f&amp;quot;Bearer <span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>bot_bearer_token</span><span class=pl-kos>}</span></span>&amp;quot;</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-respond_to_teams-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-respond_to_teams-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s>&amp;quot;Content-Type&amp;quot;</span>: <span class=pl-s>&amp;quot;application/json&amp;quot;</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-respond_to_teams-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-respond_to_teams-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    }</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-respond_to_teams-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-respond_to_teams-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-respond_to_teams-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-respond_to_teams-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Create message payload</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-respond_to_teams-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-respond_to_teams-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>payload</span> <span class=pl-c1>=</span> {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-respond_to_teams-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-respond_to_teams-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s>&amp;quot;type&amp;quot;</span>: <span class=pl-s>&amp;quot;message&amp;quot;</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-respond_to_teams-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-respond_to_teams-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s>&amp;quot;text&amp;quot;</span>: <span class=pl-s1>message</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-respond_to_teams-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-respond_to_teams-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    }</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-respond_to_teams-py-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-respond_to_teams-py-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-respond_to_teams-py-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-respond_to_teams-py-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Find the service URL and conversation ID from the event body</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-respond_to_teams-py-L16\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;16\&quot;></td>\n          <td id=\&quot;file-respond_to_teams-py-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>service_url</span> <span class=pl-c1>=</span> <span class=pl-s1>event_body</span>[<span class=pl-s>&amp;quot;serviceUrl&amp;quot;</span>]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-respond_to_teams-py-L17\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;17\&quot;></td>\n          <td id=\&quot;file-respond_to_teams-py-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>conversation_id</span> <span class=pl-c1>=</span> <span class=pl-s1>event_body</span>[<span class=pl-s>&amp;quot;conversation&amp;quot;</span>][<span class=pl-s>&amp;quot;id&amp;quot;</span>]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-respond_to_teams-py-L18\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;18\&quot;></td>\n          <td id=\&quot;file-respond_to_teams-py-LC18\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-respond_to_teams-py-L19\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;19\&quot;></td>\n          <td id=\&quot;file-respond_to_teams-py-LC19\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Send the message to Teams</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-respond_to_teams-py-L20\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;20\&quot;></td>\n          <td id=\&quot;file-respond_to_teams-py-LC20\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>response</span> <span class=pl-c1>=</span> <span class=pl-s1>requests</span>.<span class=pl-c1>post</span>(</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-respond_to_teams-py-L21\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;21\&quot;></td>\n          <td id=\&quot;file-respond_to_teams-py-LC21\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s>f&amp;quot;<span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>service_url</span><span class=pl-kos>}</span></span>/v3/conversations/<span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>conversation_id</span><span class=pl-kos>}</span></span>/activities&amp;quot;</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-respond_to_teams-py-L22\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;22\&quot;></td>\n          <td id=\&quot;file-respond_to_teams-py-LC22\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>headers</span><span class=pl-c1>=</span><span class=pl-s1>headers</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-respond_to_teams-py-L23\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;23\&quot;></td>\n          <td id=\&quot;file-respond_to_teams-py-LC23\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>json</span><span class=pl-c1>=</span><span class=pl-s1>payload</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-respond_to_teams-py-L24\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;24\&quot;></td>\n          <td id=\&quot;file-respond_to_teams-py-LC24\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    )        </td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/ad02312a72dea7ccf64429db15f8be43/raw/3aa81909244bec692d799d150343bfaa8432919a/respond_to_teams.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/ad02312a72dea7ccf64429db15f8be43#file-respond_to_teams-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          respond_to_teams.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-110350e56c50.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-110350e56c50.css"><div id="gist139608230" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-respond_to_teams-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="8" data-paste-markdown-skip="" data-tagsearch-path="respond_to_teams.py">
        <tbody><tr>
          <td id="file-respond_to_teams-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-respond_to_teams-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-en">respond_to_teams</span>(<span class="pl-s1">bot_bearer_token</span>, <span class="pl-s1">event_body</span>, <span class="pl-s1">message</span>):</td>
        </tr>
        <tr>
          <td id="file-respond_to_teams-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-respond_to_teams-py-LC2" class="blob-code blob-code-inner js-file-line">    </td>
        </tr>
        <tr>
          <td id="file-respond_to_teams-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-respond_to_teams-py-LC3" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Build auth headers</span></td>
        </tr>
        <tr>
          <td id="file-respond_to_teams-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-respond_to_teams-py-LC4" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">headers</span> <span class="pl-c1">=</span> {</td>
        </tr>
        <tr>
          <td id="file-respond_to_teams-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-respond_to_teams-py-LC5" class="blob-code blob-code-inner js-file-line">        <span class="pl-s">"Authorization"</span>: <span class="pl-s">f"Bearer <span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">bot_bearer_token</span><span class="pl-kos">}</span></span>"</span>,</td>
        </tr>
        <tr>
          <td id="file-respond_to_teams-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-respond_to_teams-py-LC6" class="blob-code blob-code-inner js-file-line">        <span class="pl-s">"Content-Type"</span>: <span class="pl-s">"application/json"</span>,</td>
        </tr>
        <tr>
          <td id="file-respond_to_teams-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-respond_to_teams-py-LC7" class="blob-code blob-code-inner js-file-line">    }</td>
        </tr>
        <tr>
          <td id="file-respond_to_teams-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-respond_to_teams-py-LC8" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-respond_to_teams-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-respond_to_teams-py-LC9" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Create message payload</span></td>
        </tr>
        <tr>
          <td id="file-respond_to_teams-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-respond_to_teams-py-LC10" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">payload</span> <span class="pl-c1">=</span> {</td>
        </tr>
        <tr>
          <td id="file-respond_to_teams-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-respond_to_teams-py-LC11" class="blob-code blob-code-inner js-file-line">        <span class="pl-s">"type"</span>: <span class="pl-s">"message"</span>,</td>
        </tr>
        <tr>
          <td id="file-respond_to_teams-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-respond_to_teams-py-LC12" class="blob-code blob-code-inner js-file-line">        <span class="pl-s">"text"</span>: <span class="pl-s1">message</span>,</td>
        </tr>
        <tr>
          <td id="file-respond_to_teams-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-respond_to_teams-py-LC13" class="blob-code blob-code-inner js-file-line">    }</td>
        </tr>
        <tr>
          <td id="file-respond_to_teams-py-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-respond_to_teams-py-LC14" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-respond_to_teams-py-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-respond_to_teams-py-LC15" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Find the service URL and conversation ID from the event body</span></td>
        </tr>
        <tr>
          <td id="file-respond_to_teams-py-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-respond_to_teams-py-LC16" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">service_url</span> <span class="pl-c1">=</span> <span class="pl-s1">event_body</span>[<span class="pl-s">"serviceUrl"</span>]</td>
        </tr>
        <tr>
          <td id="file-respond_to_teams-py-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-respond_to_teams-py-LC17" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">conversation_id</span> <span class="pl-c1">=</span> <span class="pl-s1">event_body</span>[<span class="pl-s">"conversation"</span>][<span class="pl-s">"id"</span>]</td>
        </tr>
        <tr>
          <td id="file-respond_to_teams-py-L18" class="blob-num js-line-number js-blob-rnum" data-line-number="18"></td>
          <td id="file-respond_to_teams-py-LC18" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-respond_to_teams-py-L19" class="blob-num js-line-number js-blob-rnum" data-line-number="19"></td>
          <td id="file-respond_to_teams-py-LC19" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Send the message to Teams</span></td>
        </tr>
        <tr>
          <td id="file-respond_to_teams-py-L20" class="blob-num js-line-number js-blob-rnum" data-line-number="20"></td>
          <td id="file-respond_to_teams-py-LC20" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">response</span> <span class="pl-c1">=</span> <span class="pl-s1">requests</span>.<span class="pl-c1">post</span>(</td>
        </tr>
        <tr>
          <td id="file-respond_to_teams-py-L21" class="blob-num js-line-number js-blob-rnum" data-line-number="21"></td>
          <td id="file-respond_to_teams-py-LC21" class="blob-code blob-code-inner js-file-line">        <span class="pl-s">f"<span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">service_url</span><span class="pl-kos">}</span></span>/v3/conversations/<span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">conversation_id</span><span class="pl-kos">}</span></span>/activities"</span>,</td>
        </tr>
        <tr>
          <td id="file-respond_to_teams-py-L22" class="blob-num js-line-number js-blob-rnum" data-line-number="22"></td>
          <td id="file-respond_to_teams-py-LC22" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">headers</span><span class="pl-c1">=</span><span class="pl-s1">headers</span>,</td>
        </tr>
        <tr>
          <td id="file-respond_to_teams-py-L23" class="blob-num js-line-number js-blob-rnum" data-line-number="23"></td>
          <td id="file-respond_to_teams-py-LC23" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">json</span><span class="pl-c1">=</span><span class="pl-s1">payload</span></td>
        </tr>
        <tr>
          <td id="file-respond_to_teams-py-L24" class="blob-num js-line-number js-blob-rnum" data-line-number="24"></td>
          <td id="file-respond_to_teams-py-LC24" class="blob-code blob-code-inner js-file-line">    )        </td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/ad02312a72dea7ccf64429db15f8be43/raw/3aa81909244bec692d799d150343bfaa8432919a/respond_to_teams.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/ad02312a72dea7ccf64429db15f8be43#file-respond_to_teams-py" class="Link--inTextBlock">
          respond_to_teams.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>Lets head back to the message_handler and continue unthreading stuff. </p><p>Our first stop is to build the &#8220;conversation&#8221; context. In a 1:1/DM context, we don&#8217;t do this at all, we&#8217;ll just return the question the user asks. However, in a Channel/Team chat, we walk back to the first message in the thread to build the response. </p><p>Lets follow the get_teams_conversation_history() function. </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist139608263\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-message_handler2-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;message_handler2.py content, created by KyMidd on 01:11AM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;8\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;message_handler2.py\&quot;>\n        <tr>\n          <td id=\&quot;file-message_handler2-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-message_handler2-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-en>message_handler</span>(<span class=pl-s1>event_body</span>, <span class=pl-s1>bot_bearer_token</span>, <span class=pl-s1>user_graph_auth_token</span>, <span class=pl-s1>bedrock_client</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-message_handler2-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-message_handler2-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># ...</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-message_handler2-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-message_handler2-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Walk the previous messages to build conversation context</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-message_handler2-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-message_handler2-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># If personal chat, we only read most recent message, on history. Too hard to break out of context</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-message_handler2-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-message_handler2-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># For Teams channels, we read messages back in the channel/post/thread/whatever-they-called-it-now</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-message_handler2-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-message_handler2-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>conversation</span> <span class=pl-c1>=</span> <span class=pl-en>get_teams_conversation_history</span>(<span class=pl-s1>user_graph_auth_token</span>, <span class=pl-s1>event_body</span>)</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/054f45fff753fec13c22b6dba3caf0a9/raw/0b2517a94ecbde3b634da05d60b9c5298444366e/message_handler2.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/054f45fff753fec13c22b6dba3caf0a9#file-message_handler2-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          message_handler2.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-110350e56c50.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-110350e56c50.css"><div id="gist139608263" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-message_handler2-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="8" data-paste-markdown-skip="" data-tagsearch-path="message_handler2.py">
        <tbody><tr>
          <td id="file-message_handler2-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-message_handler2-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-en">message_handler</span>(<span class="pl-s1">event_body</span>, <span class="pl-s1">bot_bearer_token</span>, <span class="pl-s1">user_graph_auth_token</span>, <span class="pl-s1">bedrock_client</span>):</td>
        </tr>
        <tr>
          <td id="file-message_handler2-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-message_handler2-py-LC2" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># ...</span></td>
        </tr>
        <tr>
          <td id="file-message_handler2-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-message_handler2-py-LC3" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Walk the previous messages to build conversation context</span></td>
        </tr>
        <tr>
          <td id="file-message_handler2-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-message_handler2-py-LC4" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># If personal chat, we only read most recent message, on history. Too hard to break out of context</span></td>
        </tr>
        <tr>
          <td id="file-message_handler2-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-message_handler2-py-LC5" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># For Teams channels, we read messages back in the channel/post/thread/whatever-they-called-it-now</span></td>
        </tr>
        <tr>
          <td id="file-message_handler2-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-message_handler2-py-LC6" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">conversation</span> <span class="pl-c1">=</span> <span class="pl-en">get_teams_conversation_history</span>(<span class="pl-s1">user_graph_auth_token</span>, <span class="pl-s1">event_body</span>)</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/054f45fff753fec13c22b6dba3caf0a9/raw/0b2517a94ecbde3b634da05d60b9c5298444366e/message_handler2.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/054f45fff753fec13c22b6dba3caf0a9#file-message_handler2-py" class="Link--inTextBlock">
          message_handler2.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>This function is absurdly complex due to the different data structures and logic we need to follow to get this to work. It genuinely doesn&#8217;t seem like the back-end was constructed in a logical way for folks to utilize - maybe it was built by different teams over the years, or was intentionally made vague for security purposes? Regardless, there&#8217;s a lot going on, lets walk through it. </p><p>First, on line 2, we build the headers using the user&#8217;s graph auth token. That&#8217;s the token that lets us act like the user. </p><p>Then we populate some values that are provided to us. Rather than be actually useful, we use these breadcrumb trail items soon to look up the actually useful values. On line 7 and 8 we get the replyToId (only populated if user &#8220;reply&#8221;-ing to a message, and conversation_type.</p><p>If we&#8217;re in a Teams Team/Channel, it&#8217;ll be of type &#8220;channel&#8221;, and if it&#8217;s a 1:1/DM context, it&#8217;ll be a &#8220;personal&#8221;. Why &#8220;personal&#8221;? <strong>&#175;\_(&#12484;)_/&#175;</strong></p><p>We&#8217;ll assume the conversation_type this time is a Channel message. Now, we need to know the channel ID. In a sane world, this would be provided to us. We are not in a sane world. We&#8217;re instead provided a teamId (19:&#8230;@thread.tacv2), and we need a team_id (a GUID). </p><blockquote><p>BIG SIGH</p></blockquote><p>Now, you&#8217;d think there&#8217;s an API that helps us exchange this? HAH. Nope, we need to iterate through every Team the user is a part of, and see which ones match. </p><blockquote><p>BIGGER SIGH</p></blockquote><p>I wrote a function for this, resolve_team_id_from_team_id(), lets follow that thread. </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist139608459\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-get_teams_conversation_history1-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;get_teams_conversation_history1.py content, created by KyMidd on 01:25AM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;8\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;get_teams_conversation_history1.py\&quot;>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history1-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history1-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-en>get_teams_conversation_history</span>(<span class=pl-s1>user_graph_auth_token</span>, <span class=pl-s1>event_body</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history1-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history1-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>headers</span> <span class=pl-c1>=</span> {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history1-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history1-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s>&amp;quot;Authorization&amp;quot;</span>: <span class=pl-s>f&amp;quot;Bearer <span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>user_graph_auth_token</span><span class=pl-kos>}</span></span>&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history1-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history1-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    }</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history1-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history1-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history1-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history1-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Populate the reply_to_id (if a reply) and conversation type</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history1-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history1-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>reply_to_id</span> <span class=pl-c1>=</span> <span class=pl-s1>event_body</span>.<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;replyToId&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history1-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history1-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>conversation_type</span> <span class=pl-c1>=</span> <span class=pl-s1>event_body</span>.<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;conversation&amp;quot;</span>, {}).<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;conversationType&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history1-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history1-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history1-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history1-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Look at type of conversation and build URL to fetch messages</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history1-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history1-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>if</span> <span class=pl-s1>conversation_type</span> <span class=pl-c1>==</span> <span class=pl-s>&amp;quot;channel&amp;quot;</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history1-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history1-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history1-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history1-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># Need to exchange the teamId (19:...@thread.tacv2) for a graph API compatible team_id (a GUID)</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history1-py-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history1-py-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># Ugh, teams</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history1-py-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history1-py-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>try</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history1-py-L16\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;16\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history1-py-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-c># Identify channelID (19:...@thread.tacv2)</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history1-py-L17\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;17\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history1-py-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>channel_id</span> <span class=pl-c1>=</span> <span class=pl-s1>event_body</span>[<span class=pl-s>&amp;quot;channelData&amp;quot;</span>][<span class=pl-s>&amp;quot;channel&amp;quot;</span>][<span class=pl-s>&amp;quot;id&amp;quot;</span>]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history1-py-L18\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;18\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history1-py-LC18\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history1-py-L19\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;19\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history1-py-LC19\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-c># Exchange it for graph compatible team ID</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history1-py-L20\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;20\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history1-py-LC20\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-k>try</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history1-py-L21\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;21\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history1-py-LC21\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                <span class=pl-s1>graph_compatible_ids</span> <span class=pl-c1>=</span> <span class=pl-en>resolve_team_id_from_team_id</span>(<span class=pl-s1>headers</span>, <span class=pl-s1>channel_id</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history1-py-L22\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;22\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history1-py-LC22\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-k>except</span> <span class=pl-v>Exception</span> <span class=pl-k>as</span> <span class=pl-s1>error</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history1-py-L23\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;23\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history1-py-LC23\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                <span class=pl-en>print</span>(<span class=pl-s>f&amp;quot;&#128683; Error resolving team ID from channel ID: <span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>error</span><span class=pl-kos>}</span></span>&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history1-py-L24\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;24\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history1-py-LC24\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                <span class=pl-k>raise</span> <span class=pl-s1>error</span></td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/86feef37b50fbe21fcd0779251294061/raw/76c3e2d9489fa672374ccb2c7ea1fe41f3ec73a5/get_teams_conversation_history1.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/86feef37b50fbe21fcd0779251294061#file-get_teams_conversation_history1-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          get_teams_conversation_history1.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-110350e56c50.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-110350e56c50.css"><div id="gist139608459" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-get_teams_conversation_history1-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="8" data-paste-markdown-skip="" data-tagsearch-path="get_teams_conversation_history1.py">
        <tbody><tr>
          <td id="file-get_teams_conversation_history1-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-get_teams_conversation_history1-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-en">get_teams_conversation_history</span>(<span class="pl-s1">user_graph_auth_token</span>, <span class="pl-s1">event_body</span>):</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history1-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-get_teams_conversation_history1-py-LC2" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">headers</span> <span class="pl-c1">=</span> {</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history1-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-get_teams_conversation_history1-py-LC3" class="blob-code blob-code-inner js-file-line">        <span class="pl-s">"Authorization"</span>: <span class="pl-s">f"Bearer <span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">user_graph_auth_token</span><span class="pl-kos">}</span></span>"</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history1-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-get_teams_conversation_history1-py-LC4" class="blob-code blob-code-inner js-file-line">    }</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history1-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-get_teams_conversation_history1-py-LC5" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history1-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-get_teams_conversation_history1-py-LC6" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Populate the reply_to_id (if a reply) and conversation type</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history1-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-get_teams_conversation_history1-py-LC7" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">reply_to_id</span> <span class="pl-c1">=</span> <span class="pl-s1">event_body</span>.<span class="pl-c1">get</span>(<span class="pl-s">"replyToId"</span>)</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history1-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-get_teams_conversation_history1-py-LC8" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">conversation_type</span> <span class="pl-c1">=</span> <span class="pl-s1">event_body</span>.<span class="pl-c1">get</span>(<span class="pl-s">"conversation"</span>, {}).<span class="pl-c1">get</span>(<span class="pl-s">"conversationType"</span>)</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history1-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-get_teams_conversation_history1-py-LC9" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history1-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-get_teams_conversation_history1-py-LC10" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Look at type of conversation and build URL to fetch messages</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history1-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-get_teams_conversation_history1-py-LC11" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">if</span> <span class="pl-s1">conversation_type</span> <span class="pl-c1">==</span> <span class="pl-s">"channel"</span>:</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history1-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-get_teams_conversation_history1-py-LC12" class="blob-code blob-code-inner js-file-line">        </td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history1-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-get_teams_conversation_history1-py-LC13" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># Need to exchange the teamId (19:...@thread.tacv2) for a graph API compatible team_id (a GUID)</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history1-py-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-get_teams_conversation_history1-py-LC14" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># Ugh, teams</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history1-py-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-get_teams_conversation_history1-py-LC15" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">try</span>:</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history1-py-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-get_teams_conversation_history1-py-LC16" class="blob-code blob-code-inner js-file-line">            <span class="pl-c"># Identify channelID (19:...@thread.tacv2)</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history1-py-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-get_teams_conversation_history1-py-LC17" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">channel_id</span> <span class="pl-c1">=</span> <span class="pl-s1">event_body</span>[<span class="pl-s">"channelData"</span>][<span class="pl-s">"channel"</span>][<span class="pl-s">"id"</span>]</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history1-py-L18" class="blob-num js-line-number js-blob-rnum" data-line-number="18"></td>
          <td id="file-get_teams_conversation_history1-py-LC18" class="blob-code blob-code-inner js-file-line">            </td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history1-py-L19" class="blob-num js-line-number js-blob-rnum" data-line-number="19"></td>
          <td id="file-get_teams_conversation_history1-py-LC19" class="blob-code blob-code-inner js-file-line">            <span class="pl-c"># Exchange it for graph compatible team ID</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history1-py-L20" class="blob-num js-line-number js-blob-rnum" data-line-number="20"></td>
          <td id="file-get_teams_conversation_history1-py-LC20" class="blob-code blob-code-inner js-file-line">            <span class="pl-k">try</span>:</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history1-py-L21" class="blob-num js-line-number js-blob-rnum" data-line-number="21"></td>
          <td id="file-get_teams_conversation_history1-py-LC21" class="blob-code blob-code-inner js-file-line">                <span class="pl-s1">graph_compatible_ids</span> <span class="pl-c1">=</span> <span class="pl-en">resolve_team_id_from_team_id</span>(<span class="pl-s1">headers</span>, <span class="pl-s1">channel_id</span>)</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history1-py-L22" class="blob-num js-line-number js-blob-rnum" data-line-number="22"></td>
          <td id="file-get_teams_conversation_history1-py-LC22" class="blob-code blob-code-inner js-file-line">            <span class="pl-k">except</span> <span class="pl-v">Exception</span> <span class="pl-k">as</span> <span class="pl-s1">error</span>:</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history1-py-L23" class="blob-num js-line-number js-blob-rnum" data-line-number="23"></td>
          <td id="file-get_teams_conversation_history1-py-LC23" class="blob-code blob-code-inner js-file-line">                <span class="pl-en">print</span>(<span class="pl-s">f"&#128683; Error resolving team ID from channel ID: <span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">error</span><span class="pl-kos">}</span></span>"</span>)</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history1-py-L24" class="blob-num js-line-number js-blob-rnum" data-line-number="24"></td>
          <td id="file-get_teams_conversation_history1-py-LC24" class="blob-code blob-code-inner js-file-line">                <span class="pl-k">raise</span> <span class="pl-s1">error</span></td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/86feef37b50fbe21fcd0779251294061/raw/76c3e2d9489fa672374ccb2c7ea1fe41f3ec73a5/get_teams_conversation_history1.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/86feef37b50fbe21fcd0779251294061#file-get_teams_conversation_history1-py" class="Link--inTextBlock">
          get_teams_conversation_history1.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>First of all, we look up all the Teams a user is joined to (SIGH), and then read all the teams in the json response. We fetch up to 100, which is probably not enough for some enterprises. If you have more, you&#8217;ll need to add pagination here, since we can only call 100 at a time. </p><p>On line 10, we iterate through each &#8220;team&#8221; json key to look up the team&#8217;s info, and then we need to get all the Channels in each Team (AGAIN, SIGH), line 14, and we iterate through those on line 22. </p><p>If the channel_id from the channel json matches the target_channel_id (the one we&#8217;re trying to identify from the message) matches, we know we&#8217;ve hit a winner. We return a json map of information. </p><blockquote><p>I know this methodology is ridiculous. Maybe there exists an API that can do this rapidly, without iterating (but I couldn&#8217;t find it). </p></blockquote><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist139608574\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-resolve_team_id_from_team_id-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;resolve_team_id_from_team_id.py content, created by KyMidd on 01:34AM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;8\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;resolve_team_id_from_team_id.py\&quot;>\n        <tr>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-en>resolve_team_id_from_team_id</span>(<span class=pl-s1>headers</span>, <span class=pl-s1>target_channel_id</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Get all teams the user is a member of</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Only supports up to 100 teams for now, should add pagination if needed at some point</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>teams_response</span> <span class=pl-c1>=</span> <span class=pl-s1>requests</span>.<span class=pl-c1>get</span>(</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s>&amp;quot;https://graph.microsoft.com/v1.0/me/joinedTeams&amp;quot;</span>, <span class=pl-s1>headers</span><span class=pl-c1>=</span><span class=pl-s1>headers</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>teams_response</span>.<span class=pl-c1>raise_for_status</span>()</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>teams</span> <span class=pl-c1>=</span> <span class=pl-s1>teams_response</span>.<span class=pl-c1>json</span>()</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># For each team, check each channel to find the one with the target_channel_id</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>for</span> <span class=pl-s1>team</span> <span class=pl-c1>in</span> <span class=pl-s1>teams</span>.<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;value&amp;quot;</span>, []):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>team_id</span> <span class=pl-c1>=</span> <span class=pl-s1>team</span>[<span class=pl-s>&amp;quot;id&amp;quot;</span>]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>team_name</span> <span class=pl-c1>=</span> <span class=pl-s1>team</span>.<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;displayName&amp;quot;</span>, <span class=pl-s>&amp;quot;&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>channels_response</span> <span class=pl-c1>=</span> <span class=pl-s1>requests</span>.<span class=pl-c1>get</span>(</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s>f&amp;quot;https://graph.microsoft.com/v1.0/teams/<span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>team_id</span><span class=pl-kos>}</span></span>/channels&amp;quot;</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-L16\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;16\&quot;></td>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>headers</span><span class=pl-c1>=</span><span class=pl-s1>headers</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-L17\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;17\&quot;></td>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        )</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-L18\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;18\&quot;></td>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-LC18\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>channels_response</span>.<span class=pl-c1>raise_for_status</span>()</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-L19\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;19\&quot;></td>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-LC19\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>channels</span> <span class=pl-c1>=</span> <span class=pl-s1>channels_response</span>.<span class=pl-c1>json</span>()</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-L20\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;20\&quot;></td>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-LC20\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-L21\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;21\&quot;></td>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-LC21\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># Iterate through channels to find the one with the target_channel_id</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-L22\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;22\&quot;></td>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-LC22\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>for</span> <span class=pl-s1>channel</span> <span class=pl-c1>in</span> <span class=pl-s1>channels</span>.<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;value&amp;quot;</span>, []):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-L23\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;23\&quot;></td>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-LC23\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-k>if</span> <span class=pl-s1>channel</span>[<span class=pl-s>&amp;quot;id&amp;quot;</span>] <span class=pl-c1>==</span> <span class=pl-s1>target_channel_id</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-L24\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;24\&quot;></td>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-LC24\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                <span class=pl-s1>channel_name</span> <span class=pl-c1>=</span> <span class=pl-s1>channel</span>.<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;displayName&amp;quot;</span>, <span class=pl-s>&amp;quot;&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-L25\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;25\&quot;></td>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-LC25\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                <span class=pl-k>return</span> {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-L26\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;26\&quot;></td>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-LC26\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                    <span class=pl-s>&amp;quot;team_id&amp;quot;</span>: <span class=pl-s1>team_id</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-L27\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;27\&quot;></td>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-LC27\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                    <span class=pl-s>&amp;quot;channel_id&amp;quot;</span>: <span class=pl-s1>target_channel_id</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-L28\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;28\&quot;></td>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-LC28\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                    <span class=pl-s>&amp;quot;team_name&amp;quot;</span>: <span class=pl-s1>team_name</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-L29\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;29\&quot;></td>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-LC29\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                    <span class=pl-s>&amp;quot;channel_name&amp;quot;</span>: <span class=pl-s1>channel_name</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-L30\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;30\&quot;></td>\n          <td id=\&quot;file-resolve_team_id_from_team_id-py-LC30\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                }</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/e04799b7262366229e57a7dad32dafbe/raw/cdfeda7685c99663fd74286bdaab322adaf22bc6/resolve_team_id_from_team_id.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/e04799b7262366229e57a7dad32dafbe#file-resolve_team_id_from_team_id-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          resolve_team_id_from_team_id.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-110350e56c50.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-110350e56c50.css"><div id="gist139608574" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-resolve_team_id_from_team_id-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="8" data-paste-markdown-skip="" data-tagsearch-path="resolve_team_id_from_team_id.py">
        <tbody><tr>
          <td id="file-resolve_team_id_from_team_id-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-resolve_team_id_from_team_id-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-en">resolve_team_id_from_team_id</span>(<span class="pl-s1">headers</span>, <span class="pl-s1">target_channel_id</span>):</td>
        </tr>
        <tr>
          <td id="file-resolve_team_id_from_team_id-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-resolve_team_id_from_team_id-py-LC2" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Get all teams the user is a member of</span></td>
        </tr>
        <tr>
          <td id="file-resolve_team_id_from_team_id-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-resolve_team_id_from_team_id-py-LC3" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Only supports up to 100 teams for now, should add pagination if needed at some point</span></td>
        </tr>
        <tr>
          <td id="file-resolve_team_id_from_team_id-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-resolve_team_id_from_team_id-py-LC4" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">teams_response</span> <span class="pl-c1">=</span> <span class="pl-s1">requests</span>.<span class="pl-c1">get</span>(</td>
        </tr>
        <tr>
          <td id="file-resolve_team_id_from_team_id-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-resolve_team_id_from_team_id-py-LC5" class="blob-code blob-code-inner js-file-line">        <span class="pl-s">"https://graph.microsoft.com/v1.0/me/joinedTeams"</span>, <span class="pl-s1">headers</span><span class="pl-c1">=</span><span class="pl-s1">headers</span>)</td>
        </tr>
        <tr>
          <td id="file-resolve_team_id_from_team_id-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-resolve_team_id_from_team_id-py-LC6" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">teams_response</span>.<span class="pl-c1">raise_for_status</span>()</td>
        </tr>
        <tr>
          <td id="file-resolve_team_id_from_team_id-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-resolve_team_id_from_team_id-py-LC7" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">teams</span> <span class="pl-c1">=</span> <span class="pl-s1">teams_response</span>.<span class="pl-c1">json</span>()</td>
        </tr>
        <tr>
          <td id="file-resolve_team_id_from_team_id-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-resolve_team_id_from_team_id-py-LC8" class="blob-code blob-code-inner js-file-line">    </td>
        </tr>
        <tr>
          <td id="file-resolve_team_id_from_team_id-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-resolve_team_id_from_team_id-py-LC9" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># For each team, check each channel to find the one with the target_channel_id</span></td>
        </tr>
        <tr>
          <td id="file-resolve_team_id_from_team_id-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-resolve_team_id_from_team_id-py-LC10" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">for</span> <span class="pl-s1">team</span> <span class="pl-c1">in</span> <span class="pl-s1">teams</span>.<span class="pl-c1">get</span>(<span class="pl-s">"value"</span>, []):</td>
        </tr>
        <tr>
          <td id="file-resolve_team_id_from_team_id-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-resolve_team_id_from_team_id-py-LC11" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">team_id</span> <span class="pl-c1">=</span> <span class="pl-s1">team</span>[<span class="pl-s">"id"</span>]</td>
        </tr>
        <tr>
          <td id="file-resolve_team_id_from_team_id-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-resolve_team_id_from_team_id-py-LC12" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">team_name</span> <span class="pl-c1">=</span> <span class="pl-s1">team</span>.<span class="pl-c1">get</span>(<span class="pl-s">"displayName"</span>, <span class="pl-s">""</span>)</td>
        </tr>
        <tr>
          <td id="file-resolve_team_id_from_team_id-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-resolve_team_id_from_team_id-py-LC13" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-resolve_team_id_from_team_id-py-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-resolve_team_id_from_team_id-py-LC14" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">channels_response</span> <span class="pl-c1">=</span> <span class="pl-s1">requests</span>.<span class="pl-c1">get</span>(</td>
        </tr>
        <tr>
          <td id="file-resolve_team_id_from_team_id-py-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-resolve_team_id_from_team_id-py-LC15" class="blob-code blob-code-inner js-file-line">            <span class="pl-s">f"https://graph.microsoft.com/v1.0/teams/<span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">team_id</span><span class="pl-kos">}</span></span>/channels"</span>,</td>
        </tr>
        <tr>
          <td id="file-resolve_team_id_from_team_id-py-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-resolve_team_id_from_team_id-py-LC16" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">headers</span><span class="pl-c1">=</span><span class="pl-s1">headers</span></td>
        </tr>
        <tr>
          <td id="file-resolve_team_id_from_team_id-py-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-resolve_team_id_from_team_id-py-LC17" class="blob-code blob-code-inner js-file-line">        )</td>
        </tr>
        <tr>
          <td id="file-resolve_team_id_from_team_id-py-L18" class="blob-num js-line-number js-blob-rnum" data-line-number="18"></td>
          <td id="file-resolve_team_id_from_team_id-py-LC18" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">channels_response</span>.<span class="pl-c1">raise_for_status</span>()</td>
        </tr>
        <tr>
          <td id="file-resolve_team_id_from_team_id-py-L19" class="blob-num js-line-number js-blob-rnum" data-line-number="19"></td>
          <td id="file-resolve_team_id_from_team_id-py-LC19" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">channels</span> <span class="pl-c1">=</span> <span class="pl-s1">channels_response</span>.<span class="pl-c1">json</span>()</td>
        </tr>
        <tr>
          <td id="file-resolve_team_id_from_team_id-py-L20" class="blob-num js-line-number js-blob-rnum" data-line-number="20"></td>
          <td id="file-resolve_team_id_from_team_id-py-LC20" class="blob-code blob-code-inner js-file-line">        </td>
        </tr>
        <tr>
          <td id="file-resolve_team_id_from_team_id-py-L21" class="blob-num js-line-number js-blob-rnum" data-line-number="21"></td>
          <td id="file-resolve_team_id_from_team_id-py-LC21" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># Iterate through channels to find the one with the target_channel_id</span></td>
        </tr>
        <tr>
          <td id="file-resolve_team_id_from_team_id-py-L22" class="blob-num js-line-number js-blob-rnum" data-line-number="22"></td>
          <td id="file-resolve_team_id_from_team_id-py-LC22" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">for</span> <span class="pl-s1">channel</span> <span class="pl-c1">in</span> <span class="pl-s1">channels</span>.<span class="pl-c1">get</span>(<span class="pl-s">"value"</span>, []):</td>
        </tr>
        <tr>
          <td id="file-resolve_team_id_from_team_id-py-L23" class="blob-num js-line-number js-blob-rnum" data-line-number="23"></td>
          <td id="file-resolve_team_id_from_team_id-py-LC23" class="blob-code blob-code-inner js-file-line">            <span class="pl-k">if</span> <span class="pl-s1">channel</span>[<span class="pl-s">"id"</span>] <span class="pl-c1">==</span> <span class="pl-s1">target_channel_id</span>:</td>
        </tr>
        <tr>
          <td id="file-resolve_team_id_from_team_id-py-L24" class="blob-num js-line-number js-blob-rnum" data-line-number="24"></td>
          <td id="file-resolve_team_id_from_team_id-py-LC24" class="blob-code blob-code-inner js-file-line">                <span class="pl-s1">channel_name</span> <span class="pl-c1">=</span> <span class="pl-s1">channel</span>.<span class="pl-c1">get</span>(<span class="pl-s">"displayName"</span>, <span class="pl-s">""</span>)</td>
        </tr>
        <tr>
          <td id="file-resolve_team_id_from_team_id-py-L25" class="blob-num js-line-number js-blob-rnum" data-line-number="25"></td>
          <td id="file-resolve_team_id_from_team_id-py-LC25" class="blob-code blob-code-inner js-file-line">                <span class="pl-k">return</span> {</td>
        </tr>
        <tr>
          <td id="file-resolve_team_id_from_team_id-py-L26" class="blob-num js-line-number js-blob-rnum" data-line-number="26"></td>
          <td id="file-resolve_team_id_from_team_id-py-LC26" class="blob-code blob-code-inner js-file-line">                    <span class="pl-s">"team_id"</span>: <span class="pl-s1">team_id</span>,</td>
        </tr>
        <tr>
          <td id="file-resolve_team_id_from_team_id-py-L27" class="blob-num js-line-number js-blob-rnum" data-line-number="27"></td>
          <td id="file-resolve_team_id_from_team_id-py-LC27" class="blob-code blob-code-inner js-file-line">                    <span class="pl-s">"channel_id"</span>: <span class="pl-s1">target_channel_id</span>,</td>
        </tr>
        <tr>
          <td id="file-resolve_team_id_from_team_id-py-L28" class="blob-num js-line-number js-blob-rnum" data-line-number="28"></td>
          <td id="file-resolve_team_id_from_team_id-py-LC28" class="blob-code blob-code-inner js-file-line">                    <span class="pl-s">"team_name"</span>: <span class="pl-s1">team_name</span>,</td>
        </tr>
        <tr>
          <td id="file-resolve_team_id_from_team_id-py-L29" class="blob-num js-line-number js-blob-rnum" data-line-number="29"></td>
          <td id="file-resolve_team_id_from_team_id-py-LC29" class="blob-code blob-code-inner js-file-line">                    <span class="pl-s">"channel_name"</span>: <span class="pl-s1">channel_name</span></td>
        </tr>
        <tr>
          <td id="file-resolve_team_id_from_team_id-py-L30" class="blob-num js-line-number js-blob-rnum" data-line-number="30"></td>
          <td id="file-resolve_team_id_from_team_id-py-LC30" class="blob-code blob-code-inner js-file-line">                }</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/e04799b7262366229e57a7dad32dafbe/raw/cdfeda7685c99663fd74286bdaab322adaf22bc6/resolve_team_id_from_team_id.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/e04799b7262366229e57a7dad32dafbe#file-resolve_team_id_from_team_id-py" class="Link--inTextBlock">
          resolve_team_id_from_team_id.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>Back to the get_teams_conversation_history() function, and we now have a valid graph-ID compatible Team and Channel ID. Now we need to read the conversation ID to identify the parent message. </p><p>This is interestingly (sadly) not called out as a separate attribute, it&#8217;s instead encoded as part of the (non-graph) conversation ID, which we extract on line 11. </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist139651813\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-get_teams_conversation_history2-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;get_teams_conversation_history2.py content, created by KyMidd on 03:34PM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;8\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;get_teams_conversation_history2.py\&quot;>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history2-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history2-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-en>get_teams_conversation_history</span>(<span class=pl-s1>user_graph_auth_token</span>, <span class=pl-s1>event_body</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history2-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history2-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c>#...</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history2-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history2-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Look at type of conversation and build URL to fetch messages</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history2-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history2-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>if</span> <span class=pl-s1>conversation_type</span> <span class=pl-c1>==</span> <span class=pl-s>&amp;quot;channel&amp;quot;</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history2-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history2-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c>#...        </span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history2-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history2-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># Read conversation ID, find the parent message ID of the conversaton &amp;quot;Post&amp;quot;/thread</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history2-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history2-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>try</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history2-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history2-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>full_convo_id</span> <span class=pl-c1>=</span> <span class=pl-s1>event_body</span>.<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;conversation&amp;quot;</span>, {}).<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;id&amp;quot;</span>, <span class=pl-s>&amp;quot;&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history2-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history2-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>parent_message_id</span> <span class=pl-c1>=</span> <span class=pl-c1>None</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history2-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history2-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-k>if</span> <span class=pl-s>&amp;quot;;messageid=&amp;quot;</span> <span class=pl-c1>in</span> <span class=pl-s1>full_convo_id</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history2-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history2-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                <span class=pl-s1>parent_message_id</span> <span class=pl-c1>=</span> <span class=pl-s1>full_convo_id</span>.<span class=pl-c1>split</span>(<span class=pl-s>&amp;quot;;messageid=&amp;quot;</span>)[<span class=pl-c1>-</span><span class=pl-c1>1</span>]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history2-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history2-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-k>else</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history2-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history2-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                <span class=pl-k>raise</span> <span class=pl-en>Exception</span>(<span class=pl-s>&amp;quot;&#10067; Could not extract root message ID from conversation ID&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history2-py-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history2-py-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history2-py-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history2-py-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>except</span> <span class=pl-v>Exception</span> <span class=pl-k>as</span> <span class=pl-s1>error</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history2-py-L16\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;16\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history2-py-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-en>print</span>(<span class=pl-s>f&amp;quot;&#128683; Error extracting root message ID: <span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>error</span><span class=pl-kos>}</span></span>&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history2-py-L17\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;17\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history2-py-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-k>raise</span></td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/78a6e3b3cb15fbd64a6fbd4612cc90fa/raw/215cbefd9935462e40b014143faf3f3b3dcfb5eb/get_teams_conversation_history2.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/78a6e3b3cb15fbd64a6fbd4612cc90fa#file-get_teams_conversation_history2-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          get_teams_conversation_history2.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-110350e56c50.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-110350e56c50.css"><div id="gist139651813" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-get_teams_conversation_history2-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="8" data-paste-markdown-skip="" data-tagsearch-path="get_teams_conversation_history2.py">
        <tbody><tr>
          <td id="file-get_teams_conversation_history2-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-get_teams_conversation_history2-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-en">get_teams_conversation_history</span>(<span class="pl-s1">user_graph_auth_token</span>, <span class="pl-s1">event_body</span>):</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history2-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-get_teams_conversation_history2-py-LC2" class="blob-code blob-code-inner js-file-line">    <span class="pl-c">#...</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history2-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-get_teams_conversation_history2-py-LC3" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Look at type of conversation and build URL to fetch messages</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history2-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-get_teams_conversation_history2-py-LC4" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">if</span> <span class="pl-s1">conversation_type</span> <span class="pl-c1">==</span> <span class="pl-s">"channel"</span>:</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history2-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-get_teams_conversation_history2-py-LC5" class="blob-code blob-code-inner js-file-line">        <span class="pl-c">#...        </span></td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history2-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-get_teams_conversation_history2-py-LC6" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># Read conversation ID, find the parent message ID of the conversaton "Post"/thread</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history2-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-get_teams_conversation_history2-py-LC7" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">try</span>:</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history2-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-get_teams_conversation_history2-py-LC8" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">full_convo_id</span> <span class="pl-c1">=</span> <span class="pl-s1">event_body</span>.<span class="pl-c1">get</span>(<span class="pl-s">"conversation"</span>, {}).<span class="pl-c1">get</span>(<span class="pl-s">"id"</span>, <span class="pl-s">""</span>)</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history2-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-get_teams_conversation_history2-py-LC9" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">parent_message_id</span> <span class="pl-c1">=</span> <span class="pl-c1">None</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history2-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-get_teams_conversation_history2-py-LC10" class="blob-code blob-code-inner js-file-line">            <span class="pl-k">if</span> <span class="pl-s">";messageid="</span> <span class="pl-c1">in</span> <span class="pl-s1">full_convo_id</span>:</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history2-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-get_teams_conversation_history2-py-LC11" class="blob-code blob-code-inner js-file-line">                <span class="pl-s1">parent_message_id</span> <span class="pl-c1">=</span> <span class="pl-s1">full_convo_id</span>.<span class="pl-c1">split</span>(<span class="pl-s">";messageid="</span>)[<span class="pl-c1">-</span><span class="pl-c1">1</span>]</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history2-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-get_teams_conversation_history2-py-LC12" class="blob-code blob-code-inner js-file-line">            <span class="pl-k">else</span>:</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history2-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-get_teams_conversation_history2-py-LC13" class="blob-code blob-code-inner js-file-line">                <span class="pl-k">raise</span> <span class="pl-en">Exception</span>(<span class="pl-s">"&#10067; Could not extract root message ID from conversation ID"</span>)</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history2-py-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-get_teams_conversation_history2-py-LC14" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history2-py-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-get_teams_conversation_history2-py-LC15" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">except</span> <span class="pl-v">Exception</span> <span class="pl-k">as</span> <span class="pl-s1">error</span>:</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history2-py-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-get_teams_conversation_history2-py-LC16" class="blob-code blob-code-inner js-file-line">            <span class="pl-en">print</span>(<span class="pl-s">f"&#128683; Error extracting root message ID: <span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">error</span><span class="pl-kos">}</span></span>"</span>)</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history2-py-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-get_teams_conversation_history2-py-LC17" class="blob-code blob-code-inner js-file-line">            <span class="pl-k">raise</span></td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/78a6e3b3cb15fbd64a6fbd4612cc90fa/raw/215cbefd9935462e40b014143faf3f3b3dcfb5eb/get_teams_conversation_history2.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/78a6e3b3cb15fbd64a6fbd4612cc90fa#file-get_teams_conversation_history2-py" class="Link--inTextBlock">
          get_teams_conversation_history2.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>Now that we know the parent message ID, we can get all the replies to that message via graph, line 6. </p><p>We encode it as json, line 12, and extract the messages values on line 13. </p><p>The messages are encoded in newest first, which is odd to me (?). Slack encodes replies in order, chronologically, Teams encodes them as newest first. That&#8217;s easy to fix with python though, we just reverse them, line 16. </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist139652033\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-get_teams_conversation_history3-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;get_teams_conversation_history3.py content, created by KyMidd on 03:43PM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;8\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;get_teams_conversation_history3.py\&quot;>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history3-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history3-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-en>get_teams_conversation_history</span>(<span class=pl-s1>user_graph_auth_token</span>, <span class=pl-s1>event_body</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history3-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history3-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c>#...</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history3-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history3-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>if</span> <span class=pl-s1>conversation_type</span> <span class=pl-c1>==</span> <span class=pl-s>&amp;quot;channel&amp;quot;</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history3-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history3-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c>#...</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history3-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history3-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># Fetch all responses in the Post/thread</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history3-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history3-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>url</span> <span class=pl-c1>=</span> <span class=pl-s>f&amp;quot;https://graph.microsoft.com/v1.0/teams/<span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>team_id</span><span class=pl-kos>}</span></span>/channels/<span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>channel_id</span><span class=pl-kos>}</span></span>/messages/<span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>parent_message_id</span><span class=pl-kos>}</span></span>/replies&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history3-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history3-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history3-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history3-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># Get previous messages</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history3-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history3-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>response</span> <span class=pl-c1>=</span> <span class=pl-s1>requests</span>.<span class=pl-c1>get</span>(<span class=pl-s1>url</span>, <span class=pl-s1>headers</span><span class=pl-c1>=</span><span class=pl-s1>headers</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history3-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history3-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>if</span> <span class=pl-s1>response</span>.<span class=pl-c1>status_code</span> <span class=pl-c1>!=</span> <span class=pl-c1>200</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history3-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history3-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-k>raise</span> <span class=pl-en>Exception</span>(<span class=pl-s>f&amp;quot;Graph API error <span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>response</span>.<span class=pl-c1>status_code</span><span class=pl-kos>}</span></span>: <span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>response</span>.<span class=pl-c1>text</span><span class=pl-kos>}</span></span>&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history3-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history3-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>data</span> <span class=pl-c1>=</span> <span class=pl-s1>response</span>.<span class=pl-c1>json</span>()</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history3-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history3-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>messages</span> <span class=pl-c1>=</span> <span class=pl-s1>data</span>.<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;value&amp;quot;</span>, [])</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history3-py-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history3-py-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history3-py-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history3-py-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># Sort so we get the oldest first</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history3-py-L16\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;16\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history3-py-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>messages</span> <span class=pl-c1>=</span> <span class=pl-en>list</span>(<span class=pl-en>reversed</span>(<span class=pl-s1>messages</span>))</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/33998915666af6910b9690094aff5436/raw/76f994965f12852d67d7c5e8fe1aaded79eee07e/get_teams_conversation_history3.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/33998915666af6910b9690094aff5436#file-get_teams_conversation_history3-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          get_teams_conversation_history3.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-110350e56c50.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-110350e56c50.css"><div id="gist139652033" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-get_teams_conversation_history3-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="8" data-paste-markdown-skip="" data-tagsearch-path="get_teams_conversation_history3.py">
        <tbody><tr>
          <td id="file-get_teams_conversation_history3-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-get_teams_conversation_history3-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-en">get_teams_conversation_history</span>(<span class="pl-s1">user_graph_auth_token</span>, <span class="pl-s1">event_body</span>):</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history3-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-get_teams_conversation_history3-py-LC2" class="blob-code blob-code-inner js-file-line">    <span class="pl-c">#...</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history3-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-get_teams_conversation_history3-py-LC3" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">if</span> <span class="pl-s1">conversation_type</span> <span class="pl-c1">==</span> <span class="pl-s">"channel"</span>:</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history3-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-get_teams_conversation_history3-py-LC4" class="blob-code blob-code-inner js-file-line">        <span class="pl-c">#...</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history3-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-get_teams_conversation_history3-py-LC5" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># Fetch all responses in the Post/thread</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history3-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-get_teams_conversation_history3-py-LC6" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">url</span> <span class="pl-c1">=</span> <span class="pl-s">f"https://graph.microsoft.com/v1.0/teams/<span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">team_id</span><span class="pl-kos">}</span></span>/channels/<span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">channel_id</span><span class="pl-kos">}</span></span>/messages/<span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">parent_message_id</span><span class="pl-kos">}</span></span>/replies"</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history3-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-get_teams_conversation_history3-py-LC7" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history3-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-get_teams_conversation_history3-py-LC8" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># Get previous messages</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history3-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-get_teams_conversation_history3-py-LC9" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">response</span> <span class="pl-c1">=</span> <span class="pl-s1">requests</span>.<span class="pl-c1">get</span>(<span class="pl-s1">url</span>, <span class="pl-s1">headers</span><span class="pl-c1">=</span><span class="pl-s1">headers</span>)</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history3-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-get_teams_conversation_history3-py-LC10" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">if</span> <span class="pl-s1">response</span>.<span class="pl-c1">status_code</span> <span class="pl-c1">!=</span> <span class="pl-c1">200</span>:</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history3-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-get_teams_conversation_history3-py-LC11" class="blob-code blob-code-inner js-file-line">            <span class="pl-k">raise</span> <span class="pl-en">Exception</span>(<span class="pl-s">f"Graph API error <span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">response</span>.<span class="pl-c1">status_code</span><span class="pl-kos">}</span></span>: <span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">response</span>.<span class="pl-c1">text</span><span class="pl-kos">}</span></span>"</span>)</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history3-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-get_teams_conversation_history3-py-LC12" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">data</span> <span class="pl-c1">=</span> <span class="pl-s1">response</span>.<span class="pl-c1">json</span>()</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history3-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-get_teams_conversation_history3-py-LC13" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">messages</span> <span class="pl-c1">=</span> <span class="pl-s1">data</span>.<span class="pl-c1">get</span>(<span class="pl-s">"value"</span>, [])</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history3-py-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-get_teams_conversation_history3-py-LC14" class="blob-code blob-code-inner js-file-line">        </td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history3-py-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-get_teams_conversation_history3-py-LC15" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># Sort so we get the oldest first</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history3-py-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-get_teams_conversation_history3-py-LC16" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">messages</span> <span class="pl-c1">=</span> <span class="pl-en">list</span>(<span class="pl-en">reversed</span>(<span class="pl-s1">messages</span>))</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/33998915666af6910b9690094aff5436/raw/76f994965f12852d67d7c5e8fe1aaded79eee07e/get_teams_conversation_history3.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/33998915666af6910b9690094aff5436#file-get_teams_conversation_history3-py" class="Link--inTextBlock">
          get_teams_conversation_history3.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>Now we have a structured json list of messages, but if you&#8217;ve been reading carefully you&#8217;ll notice the API Teams provides only lets us read all the &#8220;replies&#8221; to a parent message, not that parent message itself. So we have all messages in the thread EXCEPT the parent message, which is pretty funny in my opinion. </p><p>It&#8217;s easy enough to sort out though - we identify the root/parent message ID, line 11, then structure a graph call to get the individual message, line 14-16, then structure it as json, line 17. </p><p>Since the messages list is in chronological order, we want to insert the root message in the FIRST position, so we prepend the message on line 20. </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist139652090\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-get_teams_conversation_history4-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;get_teams_conversation_history4.py content, created by KyMidd on 03:46PM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;8\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;get_teams_conversation_history4.py\&quot;>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history4-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history4-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-en>get_teams_conversation_history</span>(<span class=pl-s1>user_graph_auth_token</span>, <span class=pl-s1>event_body</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history4-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history4-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c>#...</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history4-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history4-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>if</span> <span class=pl-s1>conversation_type</span> <span class=pl-c1>==</span> <span class=pl-s>&amp;quot;channel&amp;quot;</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history4-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history4-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c>#...</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history4-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history4-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c>### The very first message in the thread has 1 parent, which is skipped. Need to fetch it separately</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history4-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history4-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># Get the replyToId from the first message in the thread</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history4-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history4-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>if</span> <span class=pl-c1>not</span> <span class=pl-s1>messages</span> <span class=pl-c1>or</span> <span class=pl-s>&amp;quot;replyToId&amp;quot;</span> <span class=pl-c1><span class=pl-c1>not</span> <span class=pl-c1>in</span></span> <span class=pl-s1>messages</span>[<span class=pl-c1>0</span>]:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history4-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history4-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-en>print</span>(<span class=pl-s>&amp;quot;&#10067; No replyToId found in thread, can&amp;#39;t fetch root post.&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history4-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history4-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>else</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history4-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history4-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-c># Fetch the root message (the original Post)</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history4-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history4-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>root_id</span> <span class=pl-c1>=</span> <span class=pl-s1>messages</span>[<span class=pl-c1>0</span>][<span class=pl-s>&amp;quot;replyToId&amp;quot;</span>]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history4-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history4-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history4-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history4-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-c># Fetch the root message (the original Post)</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history4-py-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history4-py-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>url</span> <span class=pl-c1>=</span> <span class=pl-s>f&amp;quot;https://graph.microsoft.com/v1.0/teams/<span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>team_id</span><span class=pl-kos>}</span></span>/channels/<span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>channel_id</span><span class=pl-kos>}</span></span>/messages/<span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>root_id</span><span class=pl-kos>}</span></span>&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history4-py-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history4-py-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>resp</span> <span class=pl-c1>=</span> <span class=pl-s1>requests</span>.<span class=pl-c1>get</span>(<span class=pl-s1>url</span>, <span class=pl-s1>headers</span><span class=pl-c1>=</span><span class=pl-s1>headers</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history4-py-L16\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;16\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history4-py-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>resp</span>.<span class=pl-c1>raise_for_status</span>()</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history4-py-L17\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;17\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history4-py-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>root_post</span> <span class=pl-c1>=</span> <span class=pl-s1>resp</span>.<span class=pl-c1>json</span>()</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history4-py-L18\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;18\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history4-py-LC18\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history4-py-L19\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;19\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history4-py-LC19\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-c># Prepend it to the list</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history4-py-L20\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;20\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history4-py-LC20\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>messages</span> <span class=pl-c1>=</span> [<span class=pl-s1>root_post</span>] <span class=pl-c1>+</span> <span class=pl-s1>messages</span></td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/5a0953184579d9bc2b1b59ee1c896d01/raw/c07a37e8d15803ba9244ca95d8bc28049592fcc6/get_teams_conversation_history4.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/5a0953184579d9bc2b1b59ee1c896d01#file-get_teams_conversation_history4-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          get_teams_conversation_history4.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-110350e56c50.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-110350e56c50.css"><div id="gist139652090" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-get_teams_conversation_history4-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="8" data-paste-markdown-skip="" data-tagsearch-path="get_teams_conversation_history4.py">
        <tbody><tr>
          <td id="file-get_teams_conversation_history4-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-get_teams_conversation_history4-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-en">get_teams_conversation_history</span>(<span class="pl-s1">user_graph_auth_token</span>, <span class="pl-s1">event_body</span>):</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history4-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-get_teams_conversation_history4-py-LC2" class="blob-code blob-code-inner js-file-line">    <span class="pl-c">#...</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history4-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-get_teams_conversation_history4-py-LC3" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">if</span> <span class="pl-s1">conversation_type</span> <span class="pl-c1">==</span> <span class="pl-s">"channel"</span>:</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history4-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-get_teams_conversation_history4-py-LC4" class="blob-code blob-code-inner js-file-line">        <span class="pl-c">#...</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history4-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-get_teams_conversation_history4-py-LC5" class="blob-code blob-code-inner js-file-line">        <span class="pl-c">### The very first message in the thread has 1 parent, which is skipped. Need to fetch it separately</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history4-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-get_teams_conversation_history4-py-LC6" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># Get the replyToId from the first message in the thread</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history4-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-get_teams_conversation_history4-py-LC7" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">if</span> <span class="pl-c1">not</span> <span class="pl-s1">messages</span> <span class="pl-c1">or</span> <span class="pl-s">"replyToId"</span> <span class="pl-c1"><span class="pl-c1">not</span> <span class="pl-c1">in</span></span> <span class="pl-s1">messages</span>[<span class="pl-c1">0</span>]:</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history4-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-get_teams_conversation_history4-py-LC8" class="blob-code blob-code-inner js-file-line">            <span class="pl-en">print</span>(<span class="pl-s">"&#10067; No replyToId found in thread, can't fetch root post."</span>)</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history4-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-get_teams_conversation_history4-py-LC9" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">else</span>:</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history4-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-get_teams_conversation_history4-py-LC10" class="blob-code blob-code-inner js-file-line">            <span class="pl-c"># Fetch the root message (the original Post)</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history4-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-get_teams_conversation_history4-py-LC11" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">root_id</span> <span class="pl-c1">=</span> <span class="pl-s1">messages</span>[<span class="pl-c1">0</span>][<span class="pl-s">"replyToId"</span>]</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history4-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-get_teams_conversation_history4-py-LC12" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history4-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-get_teams_conversation_history4-py-LC13" class="blob-code blob-code-inner js-file-line">            <span class="pl-c"># Fetch the root message (the original Post)</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history4-py-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-get_teams_conversation_history4-py-LC14" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">url</span> <span class="pl-c1">=</span> <span class="pl-s">f"https://graph.microsoft.com/v1.0/teams/<span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">team_id</span><span class="pl-kos">}</span></span>/channels/<span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">channel_id</span><span class="pl-kos">}</span></span>/messages/<span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">root_id</span><span class="pl-kos">}</span></span>"</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history4-py-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-get_teams_conversation_history4-py-LC15" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">resp</span> <span class="pl-c1">=</span> <span class="pl-s1">requests</span>.<span class="pl-c1">get</span>(<span class="pl-s1">url</span>, <span class="pl-s1">headers</span><span class="pl-c1">=</span><span class="pl-s1">headers</span>)</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history4-py-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-get_teams_conversation_history4-py-LC16" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">resp</span>.<span class="pl-c1">raise_for_status</span>()</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history4-py-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-get_teams_conversation_history4-py-LC17" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">root_post</span> <span class="pl-c1">=</span> <span class="pl-s1">resp</span>.<span class="pl-c1">json</span>()</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history4-py-L18" class="blob-num js-line-number js-blob-rnum" data-line-number="18"></td>
          <td id="file-get_teams_conversation_history4-py-LC18" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history4-py-L19" class="blob-num js-line-number js-blob-rnum" data-line-number="19"></td>
          <td id="file-get_teams_conversation_history4-py-LC19" class="blob-code blob-code-inner js-file-line">            <span class="pl-c"># Prepend it to the list</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history4-py-L20" class="blob-num js-line-number js-blob-rnum" data-line-number="20"></td>
          <td id="file-get_teams_conversation_history4-py-LC20" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">messages</span> <span class="pl-c1">=</span> [<span class="pl-s1">root_post</span>] <span class="pl-c1">+</span> <span class="pl-s1">messages</span></td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/5a0953184579d9bc2b1b59ee1c896d01/raw/c07a37e8d15803ba9244ca95d8bc28049592fcc6/get_teams_conversation_history4.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/5a0953184579d9bc2b1b59ee1c896d01#file-get_teams_conversation_history4-py" class="Link--inTextBlock">
          get_teams_conversation_history4.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>If the conversation type is personal, we&#8217;re in a 1:1/DM context. We can still read back messages, but there is no &#8220;root&#8221; message since threads don&#8217;t (currently) exist in the DM context in Teams. </p><p>We do all the same stuff, we just read back a static number of messages in the 1:1 chat (in the case of my code, the default is 21 messages back). </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist139652552\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-get_teams_conversation_history5-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;get_teams_conversation_history5.py content, created by KyMidd on 04:10PM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;8\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;get_teams_conversation_history5.py\&quot;>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history5-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history5-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-en>get_teams_conversation_history</span>(<span class=pl-s1>user_graph_auth_token</span>, <span class=pl-s1>event_body</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history5-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history5-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c>#...</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history5-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history5-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>if</span> <span class=pl-s1>conversation_type</span> <span class=pl-c1>==</span> <span class=pl-s>&amp;quot;channel&amp;quot;</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history5-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history5-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># ...</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history5-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history5-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>elif</span> <span class=pl-s1>conversation_type</span> <span class=pl-c1>==</span> <span class=pl-s>&amp;quot;personal&amp;quot;</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history5-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history5-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>user_aad_id</span> <span class=pl-c1>=</span> <span class=pl-s1>event_body</span>[<span class=pl-s>&amp;quot;from&amp;quot;</span>][<span class=pl-s>&amp;quot;aadObjectId&amp;quot;</span>]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history5-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history5-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history5-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history5-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># If replying to message</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history5-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history5-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>if</span> <span class=pl-s1>reply_to_id</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history5-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history5-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>chat_id</span> <span class=pl-c1>=</span> <span class=pl-s1>event_body</span>[<span class=pl-s>&amp;quot;conversation&amp;quot;</span>][<span class=pl-s>&amp;quot;id&amp;quot;</span>]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history5-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history5-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>else</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history5-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history5-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-c># Find graph API compatible chat_id from conversation_id (Teams APIs are weird)</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history5-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history5-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>chat_id</span> <span class=pl-c1>=</span> <span class=pl-en>resolve_chat_id_from_installed_apps</span>(<span class=pl-s1>user_graph_auth_token</span>, <span class=pl-s1>user_aad_id</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history5-py-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history5-py-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history5-py-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history5-py-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># Read back the most recent messages in the personal chat</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history5-py-L16\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;16\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history5-py-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>url</span> <span class=pl-c1>=</span> <span class=pl-s>f&amp;quot;https://graph.microsoft.com/v1.0/chats/<span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>chat_id</span><span class=pl-kos>}</span></span>/messages?$top=<span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>teams_dm_conversation_read_msg_count</span><span class=pl-kos>}</span></span>&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history5-py-L17\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;17\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history5-py-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history5-py-L18\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;18\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history5-py-LC18\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># Get previous messages</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history5-py-L19\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;19\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history5-py-LC19\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>response</span> <span class=pl-c1>=</span> <span class=pl-s1>requests</span>.<span class=pl-c1>get</span>(<span class=pl-s1>url</span>, <span class=pl-s1>headers</span><span class=pl-c1>=</span><span class=pl-s1>headers</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history5-py-L20\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;20\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history5-py-LC20\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>if</span> <span class=pl-s1>response</span>.<span class=pl-c1>status_code</span> <span class=pl-c1>!=</span> <span class=pl-c1>200</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history5-py-L21\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;21\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history5-py-LC21\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-k>raise</span> <span class=pl-en>Exception</span>(<span class=pl-s>f&amp;quot;Graph API error <span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>response</span>.<span class=pl-c1>status_code</span><span class=pl-kos>}</span></span>: <span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>response</span>.<span class=pl-c1>text</span><span class=pl-kos>}</span></span>&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history5-py-L22\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;22\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history5-py-LC22\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>data</span> <span class=pl-c1>=</span> <span class=pl-s1>response</span>.<span class=pl-c1>json</span>()</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history5-py-L23\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;23\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history5-py-LC23\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>messages</span> <span class=pl-c1>=</span> <span class=pl-s1>data</span>.<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;value&amp;quot;</span>, [])</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history5-py-L24\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;24\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history5-py-LC24\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history5-py-L25\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;25\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history5-py-LC25\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>         <span class=pl-c># Sort so we get the oldest first</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history5-py-L26\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;26\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history5-py-LC26\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>messages</span> <span class=pl-c1>=</span> <span class=pl-en>list</span>(<span class=pl-en>reversed</span>(<span class=pl-s1>messages</span>))</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/aaa0b0a5922fea3d969c180ab1a0cd84/raw/c4aeef334874e08924cd505703bef0a2a4710817/get_teams_conversation_history5.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/aaa0b0a5922fea3d969c180ab1a0cd84#file-get_teams_conversation_history5-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          get_teams_conversation_history5.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-110350e56c50.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-110350e56c50.css"><div id="gist139652552" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-get_teams_conversation_history5-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="8" data-paste-markdown-skip="" data-tagsearch-path="get_teams_conversation_history5.py">
        <tbody><tr>
          <td id="file-get_teams_conversation_history5-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-get_teams_conversation_history5-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-en">get_teams_conversation_history</span>(<span class="pl-s1">user_graph_auth_token</span>, <span class="pl-s1">event_body</span>):</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history5-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-get_teams_conversation_history5-py-LC2" class="blob-code blob-code-inner js-file-line">    <span class="pl-c">#...</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history5-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-get_teams_conversation_history5-py-LC3" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">if</span> <span class="pl-s1">conversation_type</span> <span class="pl-c1">==</span> <span class="pl-s">"channel"</span>:</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history5-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-get_teams_conversation_history5-py-LC4" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># ...</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history5-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-get_teams_conversation_history5-py-LC5" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">elif</span> <span class="pl-s1">conversation_type</span> <span class="pl-c1">==</span> <span class="pl-s">"personal"</span>:</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history5-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-get_teams_conversation_history5-py-LC6" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">user_aad_id</span> <span class="pl-c1">=</span> <span class="pl-s1">event_body</span>[<span class="pl-s">"from"</span>][<span class="pl-s">"aadObjectId"</span>]</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history5-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-get_teams_conversation_history5-py-LC7" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history5-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-get_teams_conversation_history5-py-LC8" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># If replying to message</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history5-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-get_teams_conversation_history5-py-LC9" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">if</span> <span class="pl-s1">reply_to_id</span>:</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history5-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-get_teams_conversation_history5-py-LC10" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">chat_id</span> <span class="pl-c1">=</span> <span class="pl-s1">event_body</span>[<span class="pl-s">"conversation"</span>][<span class="pl-s">"id"</span>]</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history5-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-get_teams_conversation_history5-py-LC11" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">else</span>:</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history5-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-get_teams_conversation_history5-py-LC12" class="blob-code blob-code-inner js-file-line">            <span class="pl-c"># Find graph API compatible chat_id from conversation_id (Teams APIs are weird)</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history5-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-get_teams_conversation_history5-py-LC13" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">chat_id</span> <span class="pl-c1">=</span> <span class="pl-en">resolve_chat_id_from_installed_apps</span>(<span class="pl-s1">user_graph_auth_token</span>, <span class="pl-s1">user_aad_id</span>)</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history5-py-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-get_teams_conversation_history5-py-LC14" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history5-py-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-get_teams_conversation_history5-py-LC15" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># Read back the most recent messages in the personal chat</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history5-py-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-get_teams_conversation_history5-py-LC16" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">url</span> <span class="pl-c1">=</span> <span class="pl-s">f"https://graph.microsoft.com/v1.0/chats/<span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">chat_id</span><span class="pl-kos">}</span></span>/messages?$top=<span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">teams_dm_conversation_read_msg_count</span><span class="pl-kos">}</span></span>"</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history5-py-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-get_teams_conversation_history5-py-LC17" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history5-py-L18" class="blob-num js-line-number js-blob-rnum" data-line-number="18"></td>
          <td id="file-get_teams_conversation_history5-py-LC18" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># Get previous messages</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history5-py-L19" class="blob-num js-line-number js-blob-rnum" data-line-number="19"></td>
          <td id="file-get_teams_conversation_history5-py-LC19" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">response</span> <span class="pl-c1">=</span> <span class="pl-s1">requests</span>.<span class="pl-c1">get</span>(<span class="pl-s1">url</span>, <span class="pl-s1">headers</span><span class="pl-c1">=</span><span class="pl-s1">headers</span>)</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history5-py-L20" class="blob-num js-line-number js-blob-rnum" data-line-number="20"></td>
          <td id="file-get_teams_conversation_history5-py-LC20" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">if</span> <span class="pl-s1">response</span>.<span class="pl-c1">status_code</span> <span class="pl-c1">!=</span> <span class="pl-c1">200</span>:</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history5-py-L21" class="blob-num js-line-number js-blob-rnum" data-line-number="21"></td>
          <td id="file-get_teams_conversation_history5-py-LC21" class="blob-code blob-code-inner js-file-line">            <span class="pl-k">raise</span> <span class="pl-en">Exception</span>(<span class="pl-s">f"Graph API error <span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">response</span>.<span class="pl-c1">status_code</span><span class="pl-kos">}</span></span>: <span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">response</span>.<span class="pl-c1">text</span><span class="pl-kos">}</span></span>"</span>)</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history5-py-L22" class="blob-num js-line-number js-blob-rnum" data-line-number="22"></td>
          <td id="file-get_teams_conversation_history5-py-LC22" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">data</span> <span class="pl-c1">=</span> <span class="pl-s1">response</span>.<span class="pl-c1">json</span>()</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history5-py-L23" class="blob-num js-line-number js-blob-rnum" data-line-number="23"></td>
          <td id="file-get_teams_conversation_history5-py-LC23" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">messages</span> <span class="pl-c1">=</span> <span class="pl-s1">data</span>.<span class="pl-c1">get</span>(<span class="pl-s">"value"</span>, [])</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history5-py-L24" class="blob-num js-line-number js-blob-rnum" data-line-number="24"></td>
          <td id="file-get_teams_conversation_history5-py-LC24" class="blob-code blob-code-inner js-file-line">        </td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history5-py-L25" class="blob-num js-line-number js-blob-rnum" data-line-number="25"></td>
          <td id="file-get_teams_conversation_history5-py-LC25" class="blob-code blob-code-inner js-file-line">         <span class="pl-c"># Sort so we get the oldest first</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history5-py-L26" class="blob-num js-line-number js-blob-rnum" data-line-number="26"></td>
          <td id="file-get_teams_conversation_history5-py-LC26" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">messages</span> <span class="pl-c1">=</span> <span class="pl-en">list</span>(<span class="pl-en">reversed</span>(<span class="pl-s1">messages</span>))</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/aaa0b0a5922fea3d969c180ab1a0cd84/raw/c4aeef334874e08924cd505703bef0a2a4710817/get_teams_conversation_history5.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/aaa0b0a5922fea3d969c180ab1a0cd84#file-get_teams_conversation_history5-py" class="Link--inTextBlock">
          get_teams_conversation_history5.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><h1>Cleaning the Messages of Junk</h1><p>Of course, many of those messages are junk - the SSO Auth card, for example, or the &#8220;working hard&#8221; messages we send to let the users know we&#8217;re working on things. </p><p>We need to filter those messages out for this conversation to make more sense, and to get better results from our knowledge base dip. So we call a function to do so, called message_messages(), on line 10, and send all our messages to it. </p><p>Lets zoom in. </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist139652592\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-get_teams_conversation_history6-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;get_teams_conversation_history6.py content, created by KyMidd on 04:12PM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;8\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;get_teams_conversation_history6.py\&quot;>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history6-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history6-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-en>get_teams_conversation_history</span>(<span class=pl-s1>user_graph_auth_token</span>, <span class=pl-s1>event_body</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history6-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history6-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c>#...</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history6-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history6-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>if</span> <span class=pl-s1>conversation_type</span> <span class=pl-c1>==</span> <span class=pl-s>&amp;quot;channel&amp;quot;</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history6-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history6-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c>#...</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history6-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history6-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>elif</span> <span class=pl-s1>conversation_type</span> <span class=pl-c1>==</span> <span class=pl-s>&amp;quot;personal&amp;quot;</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history6-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history6-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c>#...</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history6-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history6-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history6-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history6-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Filter messages</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history6-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history6-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># This excludes the bot&amp;#39;s loading messages, the authentication card, and any messages which are invalid or blank</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_conversation_history6-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-get_teams_conversation_history6-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>messages</span> <span class=pl-c1>=</span> <span class=pl-en>massage_messages</span>(<span class=pl-s1>messages</span>, <span class=pl-s1>conversation_type</span>)</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/085f4777451baa40cc8a4c65a02832de/raw/62aea32de1c0f958f9e9a994e8a2cdde515cde0a/get_teams_conversation_history6.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/085f4777451baa40cc8a4c65a02832de#file-get_teams_conversation_history6-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          get_teams_conversation_history6.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-110350e56c50.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-110350e56c50.css"><div id="gist139652592" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-get_teams_conversation_history6-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="8" data-paste-markdown-skip="" data-tagsearch-path="get_teams_conversation_history6.py">
        <tbody><tr>
          <td id="file-get_teams_conversation_history6-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-get_teams_conversation_history6-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-en">get_teams_conversation_history</span>(<span class="pl-s1">user_graph_auth_token</span>, <span class="pl-s1">event_body</span>):</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history6-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-get_teams_conversation_history6-py-LC2" class="blob-code blob-code-inner js-file-line">    <span class="pl-c">#...</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history6-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-get_teams_conversation_history6-py-LC3" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">if</span> <span class="pl-s1">conversation_type</span> <span class="pl-c1">==</span> <span class="pl-s">"channel"</span>:</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history6-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-get_teams_conversation_history6-py-LC4" class="blob-code blob-code-inner js-file-line">        <span class="pl-c">#...</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history6-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-get_teams_conversation_history6-py-LC5" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">elif</span> <span class="pl-s1">conversation_type</span> <span class="pl-c1">==</span> <span class="pl-s">"personal"</span>:</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history6-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-get_teams_conversation_history6-py-LC6" class="blob-code blob-code-inner js-file-line">        <span class="pl-c">#...</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history6-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-get_teams_conversation_history6-py-LC7" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history6-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-get_teams_conversation_history6-py-LC8" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Filter messages</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history6-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-get_teams_conversation_history6-py-LC9" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># This excludes the bot's loading messages, the authentication card, and any messages which are invalid or blank</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_conversation_history6-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-get_teams_conversation_history6-py-LC10" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">messages</span> <span class="pl-c1">=</span> <span class="pl-en">massage_messages</span>(<span class="pl-s1">messages</span>, <span class="pl-s1">conversation_type</span>)</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/085f4777451baa40cc8a4c65a02832de/raw/62aea32de1c0f958f9e9a994e8a2cdde515cde0a/get_teams_conversation_history6.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/085f4777451baa40cc8a4c65a02832de#file-get_teams_conversation_history6-py" class="Link--inTextBlock">
          get_teams_conversation_history6.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>First of all, we read all the messages, and remove all the ones that contain the &#8220;loading&#8221; messages we send, line 3-6. </p><p>Then we read the messages for any SSO/auth Cards we&#8217;ve sent, lines 9-15. </p><p>Then we read for this odd message type, unknownFutureValue. This is a special value in Teams to indicate a future message type that our client doesn&#8217;t support. No idea what it means, but I&#8217;ve only seen it as junk and never containing anything useful, so we skip em. </p><p>Then on line 23, we filter for just the most recent message from the user. This is a necessary workaround to keep threads focused on a single topic. I&#8217;m hoping in the future &#8220;Channels&#8221;/&#8221;Threads&#8221; will come to the DM/1:1 context of Teams, and we can always read back threads, rather than trying to guess at separate topical boundaries in these chats. </p><p>Now cleaned, we return the messages. </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist139652621\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-massage_messages-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;massage_messages.py content, created by KyMidd on 04:14PM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;8\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;massage_messages.py\&quot;>\n        <tr>\n          <td id=\&quot;file-massage_messages-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-massage_messages-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-en>massage_messages</span>(<span class=pl-s1>messages</span>, <span class=pl-s1>conversation_type</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-massage_messages-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-massage_messages-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Exclude the bot messages that contain any of the teams_loading_responses substrings</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-massage_messages-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-massage_messages-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>messages</span> <span class=pl-c1>=</span> [</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-massage_messages-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-massage_messages-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>msg</span> <span class=pl-k>for</span> <span class=pl-s1>msg</span> <span class=pl-c1>in</span> <span class=pl-s1>messages</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-massage_messages-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-massage_messages-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>if</span> <span class=pl-c1>not</span> <span class=pl-en>any</span>(<span class=pl-s1>resp</span> <span class=pl-c1>in</span> <span class=pl-s1>msg</span>.<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;body&amp;quot;</span>, {}).<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;content&amp;quot;</span>, <span class=pl-s>&amp;quot;&amp;quot;</span>) <span class=pl-k>for</span> <span class=pl-s1>resp</span> <span class=pl-c1>in</span> <span class=pl-s1>teams_loading_responses</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-massage_messages-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-massage_messages-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    ]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-massage_messages-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-massage_messages-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Exclude any messages that are cards, can tell if attachment[0].contentType is &amp;quot;application/vnd.microsoft.card.adaptive&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-massage_messages-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-massage_messages-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># The authentication card is a card, and doesn&amp;#39;t include meaningful information</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-massage_messages-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-massage_messages-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>messages</span> <span class=pl-c1>=</span> [</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-massage_messages-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-massage_messages-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>msg</span> <span class=pl-k>for</span> <span class=pl-s1>msg</span> <span class=pl-c1>in</span> <span class=pl-s1>messages</span> </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-massage_messages-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-massage_messages-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>if</span> <span class=pl-c1>not</span> (</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-massage_messages-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-massage_messages-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>msg</span>.<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;attachments&amp;quot;</span>) <span class=pl-c1>and</span> </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-massage_messages-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-massage_messages-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>msg</span>[<span class=pl-s>&amp;quot;attachments&amp;quot;</span>] <span class=pl-c1>and</span> </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-massage_messages-py-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-massage_messages-py-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>msg</span>[<span class=pl-s>&amp;quot;attachments&amp;quot;</span>][<span class=pl-c1>0</span>].<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;contentType&amp;quot;</span>) <span class=pl-c1>==</span> <span class=pl-s>&amp;quot;application/vnd.microsoft.card.adaptive&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-massage_messages-py-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-massage_messages-py-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        )</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-massage_messages-py-L16\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;16\&quot;></td>\n          <td id=\&quot;file-massage_messages-py-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    ]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-massage_messages-py-L17\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;17\&quot;></td>\n          <td id=\&quot;file-massage_messages-py-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Exclude messages that are &amp;quot;messageType&amp;quot;: &amp;quot;unknownFutureValue&amp;quot;,</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-massage_messages-py-L18\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;18\&quot;></td>\n          <td id=\&quot;file-massage_messages-py-LC18\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>messages</span> <span class=pl-c1>=</span> [</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-massage_messages-py-L19\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;19\&quot;></td>\n          <td id=\&quot;file-massage_messages-py-LC19\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>msg</span> <span class=pl-k>for</span> <span class=pl-s1>msg</span> <span class=pl-c1>in</span> <span class=pl-s1>messages</span> </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-massage_messages-py-L20\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;20\&quot;></td>\n          <td id=\&quot;file-massage_messages-py-LC20\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>if</span> <span class=pl-s1>msg</span>.<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;messageType&amp;quot;</span>) <span class=pl-c1>!=</span> <span class=pl-s>&amp;quot;unknownFutureValue&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-massage_messages-py-L21\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;21\&quot;></td>\n          <td id=\&quot;file-massage_messages-py-LC21\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    ]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-massage_messages-py-L22\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;22\&quot;></td>\n          <td id=\&quot;file-massage_messages-py-LC22\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># If in 1:1 chat, we only want the most recent message from the user</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-massage_messages-py-L23\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;23\&quot;></td>\n          <td id=\&quot;file-massage_messages-py-LC23\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>if</span> <span class=pl-s1>conversation_type</span> <span class=pl-c1>==</span> <span class=pl-s>&amp;quot;personal&amp;quot;</span> <span class=pl-c1>and</span> <span class=pl-en>len</span>(<span class=pl-s1>messages</span>) <span class=pl-c1>&amp;gt;</span> <span class=pl-c1>1</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-massage_messages-py-L24\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;24\&quot;></td>\n          <td id=\&quot;file-massage_messages-py-LC24\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># Get the most recent message from the user</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-massage_messages-py-L25\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;25\&quot;></td>\n          <td id=\&quot;file-massage_messages-py-LC25\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>messages</span> <span class=pl-c1>=</span> [<span class=pl-s1>messages</span>[<span class=pl-c1>-</span><span class=pl-c1>1</span>]]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-massage_messages-py-L26\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;26\&quot;></td>\n          <td id=\&quot;file-massage_messages-py-LC26\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Return</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-massage_messages-py-L27\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;27\&quot;></td>\n          <td id=\&quot;file-massage_messages-py-LC27\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>return</span> <span class=pl-s1>messages</span></td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/634573cc0e3a1338e308d749a1c7d2b6/raw/3d40ee3674da3338d881b1b1be1c2a186f443b5c/massage_messages.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/634573cc0e3a1338e308d749a1c7d2b6#file-massage_messages-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          massage_messages.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-110350e56c50.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-110350e56c50.css"><div id="gist139652621" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-massage_messages-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="8" data-paste-markdown-skip="" data-tagsearch-path="massage_messages.py">
        <tbody><tr>
          <td id="file-massage_messages-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-massage_messages-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-en">massage_messages</span>(<span class="pl-s1">messages</span>, <span class="pl-s1">conversation_type</span>):</td>
        </tr>
        <tr>
          <td id="file-massage_messages-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-massage_messages-py-LC2" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Exclude the bot messages that contain any of the teams_loading_responses substrings</span></td>
        </tr>
        <tr>
          <td id="file-massage_messages-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-massage_messages-py-LC3" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">messages</span> <span class="pl-c1">=</span> [</td>
        </tr>
        <tr>
          <td id="file-massage_messages-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-massage_messages-py-LC4" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">msg</span> <span class="pl-k">for</span> <span class="pl-s1">msg</span> <span class="pl-c1">in</span> <span class="pl-s1">messages</span></td>
        </tr>
        <tr>
          <td id="file-massage_messages-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-massage_messages-py-LC5" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">if</span> <span class="pl-c1">not</span> <span class="pl-en">any</span>(<span class="pl-s1">resp</span> <span class="pl-c1">in</span> <span class="pl-s1">msg</span>.<span class="pl-c1">get</span>(<span class="pl-s">"body"</span>, {}).<span class="pl-c1">get</span>(<span class="pl-s">"content"</span>, <span class="pl-s">""</span>) <span class="pl-k">for</span> <span class="pl-s1">resp</span> <span class="pl-c1">in</span> <span class="pl-s1">teams_loading_responses</span>)</td>
        </tr>
        <tr>
          <td id="file-massage_messages-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-massage_messages-py-LC6" class="blob-code blob-code-inner js-file-line">    ]</td>
        </tr>
        <tr>
          <td id="file-massage_messages-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-massage_messages-py-LC7" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Exclude any messages that are cards, can tell if attachment[0].contentType is "application/vnd.microsoft.card.adaptive"</span></td>
        </tr>
        <tr>
          <td id="file-massage_messages-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-massage_messages-py-LC8" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># The authentication card is a card, and doesn't include meaningful information</span></td>
        </tr>
        <tr>
          <td id="file-massage_messages-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-massage_messages-py-LC9" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">messages</span> <span class="pl-c1">=</span> [</td>
        </tr>
        <tr>
          <td id="file-massage_messages-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-massage_messages-py-LC10" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">msg</span> <span class="pl-k">for</span> <span class="pl-s1">msg</span> <span class="pl-c1">in</span> <span class="pl-s1">messages</span> </td>
        </tr>
        <tr>
          <td id="file-massage_messages-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-massage_messages-py-LC11" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">if</span> <span class="pl-c1">not</span> (</td>
        </tr>
        <tr>
          <td id="file-massage_messages-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-massage_messages-py-LC12" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">msg</span>.<span class="pl-c1">get</span>(<span class="pl-s">"attachments"</span>) <span class="pl-c1">and</span> </td>
        </tr>
        <tr>
          <td id="file-massage_messages-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-massage_messages-py-LC13" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">msg</span>[<span class="pl-s">"attachments"</span>] <span class="pl-c1">and</span> </td>
        </tr>
        <tr>
          <td id="file-massage_messages-py-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-massage_messages-py-LC14" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">msg</span>[<span class="pl-s">"attachments"</span>][<span class="pl-c1">0</span>].<span class="pl-c1">get</span>(<span class="pl-s">"contentType"</span>) <span class="pl-c1">==</span> <span class="pl-s">"application/vnd.microsoft.card.adaptive"</span></td>
        </tr>
        <tr>
          <td id="file-massage_messages-py-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-massage_messages-py-LC15" class="blob-code blob-code-inner js-file-line">        )</td>
        </tr>
        <tr>
          <td id="file-massage_messages-py-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-massage_messages-py-LC16" class="blob-code blob-code-inner js-file-line">    ]</td>
        </tr>
        <tr>
          <td id="file-massage_messages-py-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-massage_messages-py-LC17" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Exclude messages that are "messageType": "unknownFutureValue",</span></td>
        </tr>
        <tr>
          <td id="file-massage_messages-py-L18" class="blob-num js-line-number js-blob-rnum" data-line-number="18"></td>
          <td id="file-massage_messages-py-LC18" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">messages</span> <span class="pl-c1">=</span> [</td>
        </tr>
        <tr>
          <td id="file-massage_messages-py-L19" class="blob-num js-line-number js-blob-rnum" data-line-number="19"></td>
          <td id="file-massage_messages-py-LC19" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">msg</span> <span class="pl-k">for</span> <span class="pl-s1">msg</span> <span class="pl-c1">in</span> <span class="pl-s1">messages</span> </td>
        </tr>
        <tr>
          <td id="file-massage_messages-py-L20" class="blob-num js-line-number js-blob-rnum" data-line-number="20"></td>
          <td id="file-massage_messages-py-LC20" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">if</span> <span class="pl-s1">msg</span>.<span class="pl-c1">get</span>(<span class="pl-s">"messageType"</span>) <span class="pl-c1">!=</span> <span class="pl-s">"unknownFutureValue"</span></td>
        </tr>
        <tr>
          <td id="file-massage_messages-py-L21" class="blob-num js-line-number js-blob-rnum" data-line-number="21"></td>
          <td id="file-massage_messages-py-LC21" class="blob-code blob-code-inner js-file-line">    ]</td>
        </tr>
        <tr>
          <td id="file-massage_messages-py-L22" class="blob-num js-line-number js-blob-rnum" data-line-number="22"></td>
          <td id="file-massage_messages-py-LC22" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># If in 1:1 chat, we only want the most recent message from the user</span></td>
        </tr>
        <tr>
          <td id="file-massage_messages-py-L23" class="blob-num js-line-number js-blob-rnum" data-line-number="23"></td>
          <td id="file-massage_messages-py-LC23" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">if</span> <span class="pl-s1">conversation_type</span> <span class="pl-c1">==</span> <span class="pl-s">"personal"</span> <span class="pl-c1">and</span> <span class="pl-en">len</span>(<span class="pl-s1">messages</span>) <span class="pl-c1">&gt;</span> <span class="pl-c1">1</span>:</td>
        </tr>
        <tr>
          <td id="file-massage_messages-py-L24" class="blob-num js-line-number js-blob-rnum" data-line-number="24"></td>
          <td id="file-massage_messages-py-LC24" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># Get the most recent message from the user</span></td>
        </tr>
        <tr>
          <td id="file-massage_messages-py-L25" class="blob-num js-line-number js-blob-rnum" data-line-number="25"></td>
          <td id="file-massage_messages-py-LC25" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">messages</span> <span class="pl-c1">=</span> [<span class="pl-s1">messages</span>[<span class="pl-c1">-</span><span class="pl-c1">1</span>]]</td>
        </tr>
        <tr>
          <td id="file-massage_messages-py-L26" class="blob-num js-line-number js-blob-rnum" data-line-number="26"></td>
          <td id="file-massage_messages-py-LC26" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Return</span></td>
        </tr>
        <tr>
          <td id="file-massage_messages-py-L27" class="blob-num js-line-number js-blob-rnum" data-line-number="27"></td>
          <td id="file-massage_messages-py-LC27" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">return</span> <span class="pl-s1">messages</span></td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/634573cc0e3a1338e308d749a1c7d2b6/raw/3d40ee3674da3338d881b1b1be1c2a186f443b5c/massage_messages.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/634573cc0e3a1338e308d749a1c7d2b6#file-massage_messages-py" class="Link--inTextBlock">
          massage_messages.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><h1>Summary</h1><p>I want to finish this out, but this article is getting HUGE, so lets call it here. In this article we covered how we can use a user&#8217;s auth token to read historical messages and their metadata from either a DM or Teams/Channel context. </p><p>In the next article we&#8217;ll talk about how we fetch attachments (it&#8217;s way more complex than you think it&#8217;ll be) and look up user&#8217;s information to build the whole conversation, then to post the response. </p><p>Good luck out there! <br>kyler</p>]]></content:encoded></item><item><title><![CDATA[🔥Building a Teams Bot with AI Capabilities - Part 4 - Receiver Lambda for OAuth2 Tokens and State🔥 ]]></title><description><![CDATA[aka, I don't want to authenticate to SSO each time I send you a message]]></description><link>https://www.letsdodevops.com/p/teamsai-4</link><guid isPermaLink="false">https://www.letsdodevops.com/p/teamsai-4</guid><dc:creator><![CDATA[Kyler Middleton]]></dc:creator><pubDate>Wed, 09 Jul 2025 14:30:59 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Dq6-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68fe7ee3-69b1-458e-a4ed-41bd5a5ea588_966x762.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote><p><em>This blog series focuses on presenting complex DevOps projects as simple and approachable via plain language and lots of pictures. You can do it!</em></p><p>These articles are supported by readers, <a href="https://www.letsdodevops.com/subscribe">please consider subscribing</a> to support me writing more of these articles &lt;3 :)</p><p>This article is part of a series of articles, because 1 article would be absolutely massive.</p></blockquote><ul><li><p>Part 1: <a href="https://www.letsdodevops.com/p/ai-teams-bot">Create an Azure Bot and App Registration</a></p></li><li><p>Part 2: <a href="https://www.letsdodevops.com/p/teamsai-2">Register Bot in Teams with Teams Developer Portal</a></p></li><li><p>Part 3: <a href="https://www.letsdodevops.com/p/teamsai-3">Delegated Permissions and Making Lambda Stateful for Oauth2</a></p></li><li><p>Part 4 (this article): Building the Receiver lambda to store tokens and state</p></li><li><p>Part 5: <a href="https://www.letsdodevops.com/p/teamsai-5">Finding messages, reading conversations in Teams</a></p></li><li><p>Part 6: <a href="https://www.letsdodevops.com/p/teamsai-6">Finding attachments and reading them into Bedrock</a></p></li></ul><p>Hey all! </p><p>In the series so far we&#8217;ve registered an App Registration (permissions), an Azure Bot (Teams back-end Bot infra and link to permissions), and built a bot in the Teams Developer Portal (register name in Teams App). We also talked about how we&#8217;ll be building this Teams app with Delegated access tokens exclusively, which means we&#8217;ll need to establish some state for the tokens and conversations. </p><p>The use case for storing those two things are very different: </p><ul><li><p><strong>Storing Conversations</strong> - We only have one function URL, so we need some routing - on first contact, we&#8217;ll push a &#8220;Card&#8221; to Teams to send users to the SSO login portal, and when the OAuth2 token is pushed to us, we&#8217;ll do our AI stuff. Since the first instance has shut down and discarded state, we&#8217;ll need to store the first contact payload to resume it when we have the token on second run! </p></li><li><p><strong>Storing Tokens</strong> - Since our app is stateless, once we receive the token and run, the lambda shuts off and we lose the token. If we store it for next run, users don&#8217;t need to provide a token on each run!</p></li></ul><p>We&#8217;ll go over this in more depth as we walk through the code. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Dq6-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68fe7ee3-69b1-458e-a4ed-41bd5a5ea588_966x762.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Dq6-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68fe7ee3-69b1-458e-a4ed-41bd5a5ea588_966x762.png 424w, https://substackcdn.com/image/fetch/$s_!Dq6-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68fe7ee3-69b1-458e-a4ed-41bd5a5ea588_966x762.png 848w, https://substackcdn.com/image/fetch/$s_!Dq6-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68fe7ee3-69b1-458e-a4ed-41bd5a5ea588_966x762.png 1272w, https://substackcdn.com/image/fetch/$s_!Dq6-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68fe7ee3-69b1-458e-a4ed-41bd5a5ea588_966x762.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Dq6-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68fe7ee3-69b1-458e-a4ed-41bd5a5ea588_966x762.png" width="966" height="762" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/68fe7ee3-69b1-458e-a4ed-41bd5a5ea588_966x762.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:762,&quot;width&quot;:966,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:150038,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.letsdodevops.com/i/167005038?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68fe7ee3-69b1-458e-a4ed-41bd5a5ea588_966x762.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Dq6-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68fe7ee3-69b1-458e-a4ed-41bd5a5ea588_966x762.png 424w, https://substackcdn.com/image/fetch/$s_!Dq6-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68fe7ee3-69b1-458e-a4ed-41bd5a5ea588_966x762.png 848w, https://substackcdn.com/image/fetch/$s_!Dq6-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68fe7ee3-69b1-458e-a4ed-41bd5a5ea588_966x762.png 1272w, https://substackcdn.com/image/fetch/$s_!Dq6-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68fe7ee3-69b1-458e-a4ed-41bd5a5ea588_966x762.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">There&#8217;s a lot going on here!!</figcaption></figure></div><p>If you don&#8217;t care about the walk-through, and would rather just skip to the codebase, we&#8217;ll be walking through this Receiver lambda code: </p><blockquote><p><a href="https://github.com/KyMidd/TeamsAIBot/blob/master/lambda/src/receiver.py">github.com/KyMidd/TeamsAIBot/blob/master/lambda/src/receiver.py</a></p></blockquote><p>We&#8217;ll have one lambda handle several different logic paths: </p><ul><li><p><strong>Teams event inbound, we don&#8217;t have a token (or token is expired)</strong></p><ul><li><p>Build &#8220;Card&#8221;, push to user</p></li><li><p>Store Conversations for pickup when token received</p></li><li><p>Direct them to SSO so they can authorize an OAuth2 token for us</p></li></ul></li><li><p>Teams event inbound, we have a token</p><ul><li><p>Build payload, pass to Worker</p></li></ul></li><li><p>OAuth2 token inbound</p><ul><li><p>Store token</p></li><li><p>Find resumed Conversation and build payload around it</p></li><li><p>Pass to Worker</p></li></ul></li></ul><p>Lets talk about the first one first (as it should be)</p><h1>Handle a Teams Call, No Token Yet</h1><p>On the very first call from a user, we don&#8217;t have a token for them. </p><p>First let&#8217;s store some constants. </p><p>On line 6, the secret the bot uses to store secrets. It should store the slack bot token and signing secret so we can decode the webhook and validate it&#8217;s&#8230; well, valid. </p><p>On line 9, the redirect function URL. We are unable to look this up due to circular logic - we can&#8217;t get it until it&#8217;s built, so terraform won&#8217;t let us do that. You can deploy this with an invalid value, just redeploy once you get a real redirect function URL. This is used to build the &#8220;Card&#8221; we&#8217;ll push to Teams users, and build a URL for redirection for the OAuth2 token. </p><p>On line 12, a CMK KMS key alias - an SSH key we&#8217;ll use to encrypt the OAuth2 tokens we get from users. These tokens can do a bunch of actions AS the user, so we need to be careful when storing them in dynamo to pass them between lambdas. </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist139244247\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-constants-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;constants.py content, created by KyMidd on 12:24AM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;8\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;constants.py\&quot;>\n        <tr>\n          <td id=\&quot;file-constants-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-constants-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c>###</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-constants-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-constants-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c># Constants</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-constants-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-constants-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c>###</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-constants-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-constants-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-constants-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-constants-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c># Secrets manager secret name. Json payload should contain SLACK_BOT_TOKEN and SLACK_SIGNING_SECRET</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-constants-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-constants-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s1>bot_secret_name</span> <span class=pl-c1>=</span> <span class=pl-s>&amp;quot;YOUR_JSON_SECRET_NAME&amp;quot;</span>  <span class=pl-c># Change this to your actual secret name in AWS Secrets Manager</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-constants-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-constants-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-constants-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-constants-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c># Receiver lambda info (meta)</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-constants-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-constants-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s1>redirect_function_url</span> <span class=pl-c1>=</span> <span class=pl-s>&amp;quot;https://XXXXXXX.lambda-url.us-east-1.on.aws&amp;quot;</span> <span class=pl-c># Change this to your actual Lambda URL</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-constants-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-constants-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-constants-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-constants-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c># CMK Key, used to encrypt the token</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-constants-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-constants-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s1>cmk_key_alias</span> <span class=pl-c1>=</span> <span class=pl-s1>os</span>.<span class=pl-c1>environ</span>.<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;CMK_ALIAS&amp;quot;</span>)</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/21fc5b93045fbe7be2f493f1276d6d41/raw/d83c69914a539d5c2bc32c2137220517a68d0691/constants.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/21fc5b93045fbe7be2f493f1276d6d41#file-constants-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          constants.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-110350e56c50.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-110350e56c50.css"><div id="gist139244247" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-constants-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="8" data-paste-markdown-skip="" data-tagsearch-path="constants.py">
        <tbody><tr>
          <td id="file-constants-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-constants-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-c">###</span></td>
        </tr>
        <tr>
          <td id="file-constants-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-constants-py-LC2" class="blob-code blob-code-inner js-file-line"><span class="pl-c"># Constants</span></td>
        </tr>
        <tr>
          <td id="file-constants-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-constants-py-LC3" class="blob-code blob-code-inner js-file-line"><span class="pl-c">###</span></td>
        </tr>
        <tr>
          <td id="file-constants-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-constants-py-LC4" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-constants-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-constants-py-LC5" class="blob-code blob-code-inner js-file-line"><span class="pl-c"># Secrets manager secret name. Json payload should contain SLACK_BOT_TOKEN and SLACK_SIGNING_SECRET</span></td>
        </tr>
        <tr>
          <td id="file-constants-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-constants-py-LC6" class="blob-code blob-code-inner js-file-line"><span class="pl-s1">bot_secret_name</span> <span class="pl-c1">=</span> <span class="pl-s">"YOUR_JSON_SECRET_NAME"</span>  <span class="pl-c"># Change this to your actual secret name in AWS Secrets Manager</span></td>
        </tr>
        <tr>
          <td id="file-constants-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-constants-py-LC7" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-constants-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-constants-py-LC8" class="blob-code blob-code-inner js-file-line"><span class="pl-c"># Receiver lambda info (meta)</span></td>
        </tr>
        <tr>
          <td id="file-constants-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-constants-py-LC9" class="blob-code blob-code-inner js-file-line"><span class="pl-s1">redirect_function_url</span> <span class="pl-c1">=</span> <span class="pl-s">"https://XXXXXXX.lambda-url.us-east-1.on.aws"</span> <span class="pl-c"># Change this to your actual Lambda URL</span></td>
        </tr>
        <tr>
          <td id="file-constants-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-constants-py-LC10" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-constants-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-constants-py-LC11" class="blob-code blob-code-inner js-file-line"><span class="pl-c"># CMK Key, used to encrypt the token</span></td>
        </tr>
        <tr>
          <td id="file-constants-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-constants-py-LC12" class="blob-code blob-code-inner js-file-line"><span class="pl-s1">cmk_key_alias</span> <span class="pl-c1">=</span> <span class="pl-s1">os</span>.<span class="pl-c1">environ</span>.<span class="pl-c1">get</span>(<span class="pl-s">"CMK_ALIAS"</span>)</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/21fc5b93045fbe7be2f493f1276d6d41/raw/d83c69914a539d5c2bc32c2137220517a68d0691/constants.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/21fc5b93045fbe7be2f493f1276d6d41#file-constants-py" class="Link--inTextBlock">
          constants.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><h2>Receive a Teams Event</h2><p>Lets start with the main handler for the lambda event - we receive the event and context from AWS when the function URL is triggered. </p><p>On line 4, we check if the event path contains /callback. If yes, this is an oauth2 token. It&#8217;s not in this use case, so skip. </p><p>We check if channelData is part of the body on line 7 - that&#8217;s one of the Teams keys. If yes, we&#8217;re working a teams event. </p><p>Print out some debugs and then call the handle_teams_event on line 17. </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist139244293\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-teams_event_no_token_lambda_handler-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;teams_event_no_token_lambda_handler.py content, created by KyMidd on 12:30AM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;8\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;teams_event_no_token_lambda_handler.py\&quot;>\n        <tr>\n          <td id=\&quot;file-teams_event_no_token_lambda_handler-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-teams_event_no_token_lambda_handler-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-c># Main lambda handler</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_event_no_token_lambda_handler-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-teams_event_no_token_lambda_handler-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-en>lambda_handler</span>(<span class=pl-s1>event</span>, <span class=pl-s1>context</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_event_no_token_lambda_handler-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-teams_event_no_token_lambda_handler-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Check if the event POSTED to URI /callback</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_event_no_token_lambda_handler-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-teams_event_no_token_lambda_handler-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>if</span> <span class=pl-s1>event</span>.<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;rawPath&amp;quot;</span>) <span class=pl-c1>==</span> <span class=pl-s>&amp;quot;/callback&amp;quot;</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_event_no_token_lambda_handler-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-teams_event_no_token_lambda_handler-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># ...</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_event_no_token_lambda_handler-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-teams_event_no_token_lambda_handler-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Check if channelData top level key is present. If yes, this is a Teams event</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_event_no_token_lambda_handler-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-teams_event_no_token_lambda_handler-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>if</span> <span class=pl-s>&amp;#39;channelData&amp;#39;</span> <span class=pl-c1>in</span> <span class=pl-s1>body</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_event_no_token_lambda_handler-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-teams_event_no_token_lambda_handler-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>      </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_event_no_token_lambda_handler-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-teams_event_no_token_lambda_handler-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># Debug</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_event_no_token_lambda_handler-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-teams_event_no_token_lambda_handler-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>if</span> <span class=pl-s1>os</span>.<span class=pl-c1>environ</span>.<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;VERA_DEBUG&amp;quot;</span>) <span class=pl-c1>==</span> <span class=pl-s>&amp;quot;True&amp;quot;</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_event_no_token_lambda_handler-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-teams_event_no_token_lambda_handler-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-en>print</span>(<span class=pl-s>&amp;quot;&#128994; Event body:&amp;quot;</span>, <span class=pl-s1>body</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_event_no_token_lambda_handler-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-teams_event_no_token_lambda_handler-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_event_no_token_lambda_handler-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-teams_event_no_token_lambda_handler-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>try</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_event_no_token_lambda_handler-py-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-teams_event_no_token_lambda_handler-py-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-en>print</span>(<span class=pl-s>&amp;quot;&#128994; Teams event detected&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_event_no_token_lambda_handler-py-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-teams_event_no_token_lambda_handler-py-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_event_no_token_lambda_handler-py-L16\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;16\&quot;></td>\n          <td id=\&quot;file-teams_event_no_token_lambda_handler-py-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-c># Handle the teams event</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_event_no_token_lambda_handler-py-L17\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;17\&quot;></td>\n          <td id=\&quot;file-teams_event_no_token_lambda_handler-py-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-en>handle_teams_event</span>(<span class=pl-s1>body</span>, <span class=pl-s1>event</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_event_no_token_lambda_handler-py-L18\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;18\&quot;></td>\n          <td id=\&quot;file-teams_event_no_token_lambda_handler-py-LC18\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>except</span> <span class=pl-v>Exception</span> <span class=pl-k>as</span> <span class=pl-s1>error</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_event_no_token_lambda_handler-py-L19\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;19\&quot;></td>\n          <td id=\&quot;file-teams_event_no_token_lambda_handler-py-LC19\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-en>print</span>(<span class=pl-s>&amp;quot;&#128683; Error handling Teams event: %s&amp;quot;</span>, <span class=pl-en>str</span>(<span class=pl-s1>error</span>))</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/8b70098675e8ecf2037119ed3dacf71d/raw/680da045adb90d60cbae2dbb7b909346eae17c0a/teams_event_no_token_lambda_handler.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/8b70098675e8ecf2037119ed3dacf71d#file-teams_event_no_token_lambda_handler-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          teams_event_no_token_lambda_handler.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-110350e56c50.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-110350e56c50.css"><div id="gist139244293" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-teams_event_no_token_lambda_handler-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="8" data-paste-markdown-skip="" data-tagsearch-path="teams_event_no_token_lambda_handler.py">
        <tbody><tr>
          <td id="file-teams_event_no_token_lambda_handler-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-teams_event_no_token_lambda_handler-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-c"># Main lambda handler</span></td>
        </tr>
        <tr>
          <td id="file-teams_event_no_token_lambda_handler-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-teams_event_no_token_lambda_handler-py-LC2" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-en">lambda_handler</span>(<span class="pl-s1">event</span>, <span class="pl-s1">context</span>):</td>
        </tr>
        <tr>
          <td id="file-teams_event_no_token_lambda_handler-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-teams_event_no_token_lambda_handler-py-LC3" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Check if the event POSTED to URI /callback</span></td>
        </tr>
        <tr>
          <td id="file-teams_event_no_token_lambda_handler-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-teams_event_no_token_lambda_handler-py-LC4" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">if</span> <span class="pl-s1">event</span>.<span class="pl-c1">get</span>(<span class="pl-s">"rawPath"</span>) <span class="pl-c1">==</span> <span class="pl-s">"/callback"</span>:</td>
        </tr>
        <tr>
          <td id="file-teams_event_no_token_lambda_handler-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-teams_event_no_token_lambda_handler-py-LC5" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># ...</span></td>
        </tr>
        <tr>
          <td id="file-teams_event_no_token_lambda_handler-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-teams_event_no_token_lambda_handler-py-LC6" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Check if channelData top level key is present. If yes, this is a Teams event</span></td>
        </tr>
        <tr>
          <td id="file-teams_event_no_token_lambda_handler-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-teams_event_no_token_lambda_handler-py-LC7" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">if</span> <span class="pl-s">'channelData'</span> <span class="pl-c1">in</span> <span class="pl-s1">body</span>:</td>
        </tr>
        <tr>
          <td id="file-teams_event_no_token_lambda_handler-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-teams_event_no_token_lambda_handler-py-LC8" class="blob-code blob-code-inner js-file-line">      </td>
        </tr>
        <tr>
          <td id="file-teams_event_no_token_lambda_handler-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-teams_event_no_token_lambda_handler-py-LC9" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># Debug</span></td>
        </tr>
        <tr>
          <td id="file-teams_event_no_token_lambda_handler-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-teams_event_no_token_lambda_handler-py-LC10" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">if</span> <span class="pl-s1">os</span>.<span class="pl-c1">environ</span>.<span class="pl-c1">get</span>(<span class="pl-s">"VERA_DEBUG"</span>) <span class="pl-c1">==</span> <span class="pl-s">"True"</span>:</td>
        </tr>
        <tr>
          <td id="file-teams_event_no_token_lambda_handler-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-teams_event_no_token_lambda_handler-py-LC11" class="blob-code blob-code-inner js-file-line">            <span class="pl-en">print</span>(<span class="pl-s">"&#128994; Event body:"</span>, <span class="pl-s1">body</span>)</td>
        </tr>
        <tr>
          <td id="file-teams_event_no_token_lambda_handler-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-teams_event_no_token_lambda_handler-py-LC12" class="blob-code blob-code-inner js-file-line">        </td>
        </tr>
        <tr>
          <td id="file-teams_event_no_token_lambda_handler-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-teams_event_no_token_lambda_handler-py-LC13" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">try</span>:</td>
        </tr>
        <tr>
          <td id="file-teams_event_no_token_lambda_handler-py-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-teams_event_no_token_lambda_handler-py-LC14" class="blob-code blob-code-inner js-file-line">            <span class="pl-en">print</span>(<span class="pl-s">"&#128994; Teams event detected"</span>)</td>
        </tr>
        <tr>
          <td id="file-teams_event_no_token_lambda_handler-py-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-teams_event_no_token_lambda_handler-py-LC15" class="blob-code blob-code-inner js-file-line">            </td>
        </tr>
        <tr>
          <td id="file-teams_event_no_token_lambda_handler-py-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-teams_event_no_token_lambda_handler-py-LC16" class="blob-code blob-code-inner js-file-line">            <span class="pl-c"># Handle the teams event</span></td>
        </tr>
        <tr>
          <td id="file-teams_event_no_token_lambda_handler-py-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-teams_event_no_token_lambda_handler-py-LC17" class="blob-code blob-code-inner js-file-line">            <span class="pl-en">handle_teams_event</span>(<span class="pl-s1">body</span>, <span class="pl-s1">event</span>)</td>
        </tr>
        <tr>
          <td id="file-teams_event_no_token_lambda_handler-py-L18" class="blob-num js-line-number js-blob-rnum" data-line-number="18"></td>
          <td id="file-teams_event_no_token_lambda_handler-py-LC18" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">except</span> <span class="pl-v">Exception</span> <span class="pl-k">as</span> <span class="pl-s1">error</span>:</td>
        </tr>
        <tr>
          <td id="file-teams_event_no_token_lambda_handler-py-L19" class="blob-num js-line-number js-blob-rnum" data-line-number="19"></td>
          <td id="file-teams_event_no_token_lambda_handler-py-LC19" class="blob-code blob-code-inner js-file-line">            <span class="pl-en">print</span>(<span class="pl-s">"&#128683; Error handling Teams event: %s"</span>, <span class="pl-en">str</span>(<span class="pl-s1">error</span>))</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/8b70098675e8ecf2037119ed3dacf71d/raw/680da045adb90d60cbae2dbb7b909346eae17c0a/teams_event_no_token_lambda_handler.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/8b70098675e8ecf2037119ed3dacf71d#file-teams_event_no_token_lambda_handler-py" class="Link--inTextBlock">
          teams_event_no_token_lambda_handler.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>The handle teams event is used to handle any Teams event that comes in, token present or not. </p><p>On line 11 and 12, we store the conversation table arn and token table arn as local vars - they&#8217;re injected as environmental variables by the terraform code. </p><p>Then on line 15 we register a dynamoDB client using the boto3 library - we&#8217;ll use this to read and write to dynamo. </p><p>Then on line 18 we read the aadObjectId from the event - this is the user that triggered the action. If the action came from Teams, it was triggered by a User or Bot, both of which are registered in Entra. </p><p>Then on line 25, we grab an encrypted token from dynamo. We have some error handling logic present in case there is no token. Lets zoom in on that. </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist139245633\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-handle_teams_event1-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;handle_teams_event1.py content, created by KyMidd on 02:21AM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;8\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;handle_teams_event1.py\&quot;>\n        <tr>\n          <td id=\&quot;file-handle_teams_event1-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-handle_teams_event1-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-en>handle_teams_event</span>(<span class=pl-s1>body</span>, <span class=pl-s1>event</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event1-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-handle_teams_event1-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Identify event_type</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event1-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-handle_teams_event1-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>event_type</span> <span class=pl-c1>=</span> <span class=pl-s1>body</span>.<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;type&amp;quot;</span>, <span class=pl-s>&amp;quot;&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event1-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-handle_teams_event1-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event1-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-handle_teams_event1-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Only process events we care about</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event1-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-handle_teams_event1-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>if</span> <span class=pl-s1>event_type</span> <span class=pl-c1>==</span> <span class=pl-s>&amp;#39;message&amp;#39;</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event1-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-handle_teams_event1-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event1-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-handle_teams_event1-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-en>print</span>(<span class=pl-s>&amp;quot;&#128994; Event type:&amp;quot;</span>, <span class=pl-s1>event_type</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event1-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-handle_teams_event1-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event1-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-handle_teams_event1-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># Read table names from environment variables</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event1-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-handle_teams_event1-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>conversation_table_arn</span> <span class=pl-c1>=</span> <span class=pl-s1>os</span>.<span class=pl-c1>environ</span>.<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;CONVERSATION_TABLE_ARN&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event1-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-handle_teams_event1-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>token_table_arn</span> <span class=pl-c1>=</span> <span class=pl-s1>os</span>.<span class=pl-c1>environ</span>.<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;TOKEN_TABLE_ARN&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event1-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-handle_teams_event1-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event1-py-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-handle_teams_event1-py-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># Check for existing valid token</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event1-py-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-handle_teams_event1-py-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>dynamodb_client</span> <span class=pl-c1>=</span> <span class=pl-s1>boto3</span>.<span class=pl-c1>client</span>(<span class=pl-s>&amp;quot;dynamodb&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event1-py-L16\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;16\&quot;></td>\n          <td id=\&quot;file-handle_teams_event1-py-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event1-py-L17\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;17\&quot;></td>\n          <td id=\&quot;file-handle_teams_event1-py-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># Get AAD ID from event</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event1-py-L18\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;18\&quot;></td>\n          <td id=\&quot;file-handle_teams_event1-py-LC18\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>aadObjectId</span> <span class=pl-c1>=</span> <span class=pl-s1>body</span>.<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;from&amp;quot;</span>, {}).<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;aadObjectId&amp;quot;</span>, <span class=pl-s>&amp;quot;&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event1-py-L19\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;19\&quot;></td>\n          <td id=\&quot;file-handle_teams_event1-py-LC19\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>if</span> <span class=pl-c1>not</span> <span class=pl-s1>aadObjectId</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event1-py-L20\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;20\&quot;></td>\n          <td id=\&quot;file-handle_teams_event1-py-LC20\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-en>print</span>(<span class=pl-s>&amp;quot;&#128683; No AAD ID found in event, exiting&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event1-py-L21\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;21\&quot;></td>\n          <td id=\&quot;file-handle_teams_event1-py-LC21\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-k>return</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event1-py-L22\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;22\&quot;></td>\n          <td id=\&quot;file-handle_teams_event1-py-LC22\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event1-py-L23\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;23\&quot;></td>\n          <td id=\&quot;file-handle_teams_event1-py-LC23\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># Check if existing token. If not, send user card to send to authentication</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event1-py-L24\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;24\&quot;></td>\n          <td id=\&quot;file-handle_teams_event1-py-LC24\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># Token is base64 encoded and also encrypted with a CMK key</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event1-py-L25\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;25\&quot;></td>\n          <td id=\&quot;file-handle_teams_event1-py-LC25\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>encrypted_token</span> <span class=pl-c1>=</span> <span class=pl-en>get_token</span>(<span class=pl-s1>dynamodb_client</span>, <span class=pl-s1>aadObjectId</span>, <span class=pl-s1>token_table_arn</span>)</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/0e7b25349cbcca0f85d3eae1befc81a7/raw/8fc0cf094e7a51456dd400c09cbb175d9f56f428/handle_teams_event1.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/0e7b25349cbcca0f85d3eae1befc81a7#file-handle_teams_event1-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          handle_teams_event1.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-110350e56c50.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-110350e56c50.css"><div id="gist139245633" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-handle_teams_event1-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="8" data-paste-markdown-skip="" data-tagsearch-path="handle_teams_event1.py">
        <tbody><tr>
          <td id="file-handle_teams_event1-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-handle_teams_event1-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-en">handle_teams_event</span>(<span class="pl-s1">body</span>, <span class="pl-s1">event</span>):</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event1-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-handle_teams_event1-py-LC2" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Identify event_type</span></td>
        </tr>
        <tr>
          <td id="file-handle_teams_event1-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-handle_teams_event1-py-LC3" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">event_type</span> <span class="pl-c1">=</span> <span class="pl-s1">body</span>.<span class="pl-c1">get</span>(<span class="pl-s">"type"</span>, <span class="pl-s">""</span>)</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event1-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-handle_teams_event1-py-LC4" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event1-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-handle_teams_event1-py-LC5" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Only process events we care about</span></td>
        </tr>
        <tr>
          <td id="file-handle_teams_event1-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-handle_teams_event1-py-LC6" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">if</span> <span class="pl-s1">event_type</span> <span class="pl-c1">==</span> <span class="pl-s">'message'</span>:</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event1-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-handle_teams_event1-py-LC7" class="blob-code blob-code-inner js-file-line">        </td>
        </tr>
        <tr>
          <td id="file-handle_teams_event1-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-handle_teams_event1-py-LC8" class="blob-code blob-code-inner js-file-line">        <span class="pl-en">print</span>(<span class="pl-s">"&#128994; Event type:"</span>, <span class="pl-s1">event_type</span>)</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event1-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-handle_teams_event1-py-LC9" class="blob-code blob-code-inner js-file-line">        </td>
        </tr>
        <tr>
          <td id="file-handle_teams_event1-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-handle_teams_event1-py-LC10" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># Read table names from environment variables</span></td>
        </tr>
        <tr>
          <td id="file-handle_teams_event1-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-handle_teams_event1-py-LC11" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">conversation_table_arn</span> <span class="pl-c1">=</span> <span class="pl-s1">os</span>.<span class="pl-c1">environ</span>.<span class="pl-c1">get</span>(<span class="pl-s">"CONVERSATION_TABLE_ARN"</span>)</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event1-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-handle_teams_event1-py-LC12" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">token_table_arn</span> <span class="pl-c1">=</span> <span class="pl-s1">os</span>.<span class="pl-c1">environ</span>.<span class="pl-c1">get</span>(<span class="pl-s">"TOKEN_TABLE_ARN"</span>)</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event1-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-handle_teams_event1-py-LC13" class="blob-code blob-code-inner js-file-line">        </td>
        </tr>
        <tr>
          <td id="file-handle_teams_event1-py-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-handle_teams_event1-py-LC14" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># Check for existing valid token</span></td>
        </tr>
        <tr>
          <td id="file-handle_teams_event1-py-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-handle_teams_event1-py-LC15" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">dynamodb_client</span> <span class="pl-c1">=</span> <span class="pl-s1">boto3</span>.<span class="pl-c1">client</span>(<span class="pl-s">"dynamodb"</span>)</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event1-py-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-handle_teams_event1-py-LC16" class="blob-code blob-code-inner js-file-line">        </td>
        </tr>
        <tr>
          <td id="file-handle_teams_event1-py-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-handle_teams_event1-py-LC17" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># Get AAD ID from event</span></td>
        </tr>
        <tr>
          <td id="file-handle_teams_event1-py-L18" class="blob-num js-line-number js-blob-rnum" data-line-number="18"></td>
          <td id="file-handle_teams_event1-py-LC18" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">aadObjectId</span> <span class="pl-c1">=</span> <span class="pl-s1">body</span>.<span class="pl-c1">get</span>(<span class="pl-s">"from"</span>, {}).<span class="pl-c1">get</span>(<span class="pl-s">"aadObjectId"</span>, <span class="pl-s">""</span>)</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event1-py-L19" class="blob-num js-line-number js-blob-rnum" data-line-number="19"></td>
          <td id="file-handle_teams_event1-py-LC19" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">if</span> <span class="pl-c1">not</span> <span class="pl-s1">aadObjectId</span>:</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event1-py-L20" class="blob-num js-line-number js-blob-rnum" data-line-number="20"></td>
          <td id="file-handle_teams_event1-py-LC20" class="blob-code blob-code-inner js-file-line">            <span class="pl-en">print</span>(<span class="pl-s">"&#128683; No AAD ID found in event, exiting"</span>)</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event1-py-L21" class="blob-num js-line-number js-blob-rnum" data-line-number="21"></td>
          <td id="file-handle_teams_event1-py-LC21" class="blob-code blob-code-inner js-file-line">            <span class="pl-k">return</span></td>
        </tr>
        <tr>
          <td id="file-handle_teams_event1-py-L22" class="blob-num js-line-number js-blob-rnum" data-line-number="22"></td>
          <td id="file-handle_teams_event1-py-LC22" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event1-py-L23" class="blob-num js-line-number js-blob-rnum" data-line-number="23"></td>
          <td id="file-handle_teams_event1-py-LC23" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># Check if existing token. If not, send user card to send to authentication</span></td>
        </tr>
        <tr>
          <td id="file-handle_teams_event1-py-L24" class="blob-num js-line-number js-blob-rnum" data-line-number="24"></td>
          <td id="file-handle_teams_event1-py-LC24" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># Token is base64 encoded and also encrypted with a CMK key</span></td>
        </tr>
        <tr>
          <td id="file-handle_teams_event1-py-L25" class="blob-num js-line-number js-blob-rnum" data-line-number="25"></td>
          <td id="file-handle_teams_event1-py-LC25" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">encrypted_token</span> <span class="pl-c1">=</span> <span class="pl-en">get_token</span>(<span class="pl-s1">dynamodb_client</span>, <span class="pl-s1">aadObjectId</span>, <span class="pl-s1">token_table_arn</span>)</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/0e7b25349cbcca0f85d3eae1befc81a7/raw/8fc0cf094e7a51456dd400c09cbb175d9f56f428/handle_teams_event1.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/0e7b25349cbcca0f85d3eae1befc81a7#file-handle_teams_event1-py" class="Link--inTextBlock">
          handle_teams_event1.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><h2>Yeah, But When Did I Cook That Token? </h2><p>This function helps us get a token and validate that it&#8217;s valid for at least 30 seconds. </p><blockquote><p>Expert tip: tokens are valid for between 60-90m, and it&#8217;s random for each token issued to avoid thundering herds expiring. Cool architecture, $MSFT. </p></blockquote><p>On line 6, we attempt to get the item from dynamo that matches our &#8220;token&#8221; dynamo, and has a key (line 8) of the aadObjectId (the user&#8217;s ID) set to a string of the aadObjectId from the event. This should make sure that users can only fetch and use their own token. Notably if there is no token, $item is simply not populated. </p><p>On line 15, we check if there&#8217;s no item set (there was no token ever stored), and if so, we return None. We have some higher level checks that set next steps. </p><p>On line 19, we lookup when the item expiresAt, an epic time-stamp. </p><p>On line 22, we check if the token expires less than 30 seconds from now. I picked 30 seconds as a common sense default of how long the whole conversation takes to run. If the token expires 31 seconds from now, we probably don&#8217;t care, and can complete this conversation before then. If it expires less than 30 seconds from now (or is already expired), we return None. </p><p>If we get to line 29, there is a valid token, so we map it to a variable, $token, and return it. </p><blockquote><p>Initially these tokens weren&#8217;t encrypted, but when I learned how powerful they were I decided to encrypt them (AES-256 using CMK KMS). We&#8217;ll talk about that soon. In future might move them to a Secrets Manager Secret&#8230;</p></blockquote><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist139245682\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-get_token-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;get_token.py content, created by KyMidd on 02:23AM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;8\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;get_token.py\&quot;>\n        <tr>\n          <td id=\&quot;file-get_token-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-get_token-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-en>get_token</span>(<span class=pl-s1>dynamodb_client</span>, <span class=pl-s1>aadObjectId</span>, <span class=pl-s1>table_name</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_token-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-get_token-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Debug</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_token-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-get_token-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>if</span> <span class=pl-s1>os</span>.<span class=pl-c1>environ</span>.<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;VERA_DEBUG&amp;quot;</span>) <span class=pl-c1>==</span> <span class=pl-s>&amp;quot;True&amp;quot;</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_token-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-get_token-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-en>print</span>(<span class=pl-s>&amp;quot;&#128994; Looking for token for AAD ObjectId:&amp;quot;</span>, <span class=pl-s1>aadObjectId</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_token-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-get_token-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_token-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-get_token-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>item</span> <span class=pl-c1>=</span> <span class=pl-s1>dynamodb_client</span>.<span class=pl-c1>get_item</span>(</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_token-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-get_token-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-v>TableName</span><span class=pl-c1>=</span><span class=pl-s1>table_name</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_token-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-get_token-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-v>Key</span><span class=pl-c1>=</span>{<span class=pl-s>&amp;quot;aadObjectId&amp;quot;</span>: {<span class=pl-s>&amp;quot;S&amp;quot;</span>: <span class=pl-s1>aadObjectId</span>}}</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_token-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-get_token-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    ).<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;Item&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_token-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-get_token-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_token-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-get_token-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Debug</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_token-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-get_token-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>if</span> <span class=pl-s1>os</span>.<span class=pl-c1>environ</span>.<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;VERA_DEBUG&amp;quot;</span>) <span class=pl-c1>==</span> <span class=pl-s>&amp;quot;True&amp;quot;</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_token-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-get_token-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-en>print</span>(<span class=pl-s>&amp;quot;&#128994; DynamoDB item found:&amp;quot;</span>, <span class=pl-s1>item</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_token-py-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-get_token-py-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_token-py-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-get_token-py-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>if</span> <span class=pl-c1>not</span> <span class=pl-s1>item</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_token-py-L16\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;16\&quot;></td>\n          <td id=\&quot;file-get_token-py-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>return</span> <span class=pl-c1>None</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_token-py-L17\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;17\&quot;></td>\n          <td id=\&quot;file-get_token-py-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_token-py-L18\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;18\&quot;></td>\n          <td id=\&quot;file-get_token-py-LC18\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Check when the token expires</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_token-py-L19\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;19\&quot;></td>\n          <td id=\&quot;file-get_token-py-LC19\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>expires_at</span> <span class=pl-c1>=</span> <span class=pl-en>int</span>(<span class=pl-s1>item</span>[<span class=pl-s>&amp;quot;expiresAt&amp;quot;</span>][<span class=pl-s>&amp;quot;N&amp;quot;</span>])</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_token-py-L20\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;20\&quot;></td>\n          <td id=\&quot;file-get_token-py-LC20\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_token-py-L21\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;21\&quot;></td>\n          <td id=\&quot;file-get_token-py-LC21\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># If token expires less than 30 seconds from now, return None</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_token-py-L22\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;22\&quot;></td>\n          <td id=\&quot;file-get_token-py-LC22\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>if</span> <span class=pl-s1>expires_at</span> <span class=pl-c1>-</span> <span class=pl-en>int</span>(<span class=pl-s1>time</span>.<span class=pl-c1>time</span>()) <span class=pl-c1>&amp;lt;=</span> <span class=pl-c1>30</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_token-py-L23\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;23\&quot;></td>\n          <td id=\&quot;file-get_token-py-LC23\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># Debug</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_token-py-L24\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;24\&quot;></td>\n          <td id=\&quot;file-get_token-py-LC24\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>if</span> <span class=pl-s1>os</span>.<span class=pl-c1>environ</span>.<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;VERA_DEBUG&amp;quot;</span>) <span class=pl-c1>==</span> <span class=pl-s>&amp;quot;True&amp;quot;</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_token-py-L25\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;25\&quot;></td>\n          <td id=\&quot;file-get_token-py-LC25\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-en>print</span>(<span class=pl-s>&amp;quot;&#128993; Token is expired or expiring in the next 30 seconds, returning None&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_token-py-L26\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;26\&quot;></td>\n          <td id=\&quot;file-get_token-py-LC26\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>return</span> <span class=pl-c1>None</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_token-py-L27\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;27\&quot;></td>\n          <td id=\&quot;file-get_token-py-LC27\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_token-py-L28\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;28\&quot;></td>\n          <td id=\&quot;file-get_token-py-LC28\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Store the token in a variable</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_token-py-L29\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;29\&quot;></td>\n          <td id=\&quot;file-get_token-py-LC29\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>token</span> <span class=pl-c1>=</span> <span class=pl-s1>item</span>[<span class=pl-s>&amp;quot;accessToken&amp;quot;</span>][<span class=pl-s>&amp;quot;S&amp;quot;</span>]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_token-py-L30\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;30\&quot;></td>\n          <td id=\&quot;file-get_token-py-LC30\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_token-py-L31\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;31\&quot;></td>\n          <td id=\&quot;file-get_token-py-LC31\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>return</span> <span class=pl-s1>token</span></td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/4383baae916f221cb40453de749bf397/raw/490d3ca11970460ce86bc3fd80fbd2e888ed09f9/get_token.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/4383baae916f221cb40453de749bf397#file-get_token-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          get_token.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-110350e56c50.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-110350e56c50.css"><div id="gist139245682" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-get_token-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="8" data-paste-markdown-skip="" data-tagsearch-path="get_token.py">
        <tbody><tr>
          <td id="file-get_token-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-get_token-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-en">get_token</span>(<span class="pl-s1">dynamodb_client</span>, <span class="pl-s1">aadObjectId</span>, <span class="pl-s1">table_name</span>):</td>
        </tr>
        <tr>
          <td id="file-get_token-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-get_token-py-LC2" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Debug</span></td>
        </tr>
        <tr>
          <td id="file-get_token-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-get_token-py-LC3" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">if</span> <span class="pl-s1">os</span>.<span class="pl-c1">environ</span>.<span class="pl-c1">get</span>(<span class="pl-s">"VERA_DEBUG"</span>) <span class="pl-c1">==</span> <span class="pl-s">"True"</span>:</td>
        </tr>
        <tr>
          <td id="file-get_token-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-get_token-py-LC4" class="blob-code blob-code-inner js-file-line">        <span class="pl-en">print</span>(<span class="pl-s">"&#128994; Looking for token for AAD ObjectId:"</span>, <span class="pl-s1">aadObjectId</span>)</td>
        </tr>
        <tr>
          <td id="file-get_token-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-get_token-py-LC5" class="blob-code blob-code-inner js-file-line">    </td>
        </tr>
        <tr>
          <td id="file-get_token-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-get_token-py-LC6" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">item</span> <span class="pl-c1">=</span> <span class="pl-s1">dynamodb_client</span>.<span class="pl-c1">get_item</span>(</td>
        </tr>
        <tr>
          <td id="file-get_token-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-get_token-py-LC7" class="blob-code blob-code-inner js-file-line">        <span class="pl-v">TableName</span><span class="pl-c1">=</span><span class="pl-s1">table_name</span>,</td>
        </tr>
        <tr>
          <td id="file-get_token-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-get_token-py-LC8" class="blob-code blob-code-inner js-file-line">        <span class="pl-v">Key</span><span class="pl-c1">=</span>{<span class="pl-s">"aadObjectId"</span>: {<span class="pl-s">"S"</span>: <span class="pl-s1">aadObjectId</span>}}</td>
        </tr>
        <tr>
          <td id="file-get_token-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-get_token-py-LC9" class="blob-code blob-code-inner js-file-line">    ).<span class="pl-c1">get</span>(<span class="pl-s">"Item"</span>)</td>
        </tr>
        <tr>
          <td id="file-get_token-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-get_token-py-LC10" class="blob-code blob-code-inner js-file-line">    </td>
        </tr>
        <tr>
          <td id="file-get_token-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-get_token-py-LC11" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Debug</span></td>
        </tr>
        <tr>
          <td id="file-get_token-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-get_token-py-LC12" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">if</span> <span class="pl-s1">os</span>.<span class="pl-c1">environ</span>.<span class="pl-c1">get</span>(<span class="pl-s">"VERA_DEBUG"</span>) <span class="pl-c1">==</span> <span class="pl-s">"True"</span>:</td>
        </tr>
        <tr>
          <td id="file-get_token-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-get_token-py-LC13" class="blob-code blob-code-inner js-file-line">        <span class="pl-en">print</span>(<span class="pl-s">"&#128994; DynamoDB item found:"</span>, <span class="pl-s1">item</span>)</td>
        </tr>
        <tr>
          <td id="file-get_token-py-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-get_token-py-LC14" class="blob-code blob-code-inner js-file-line">    </td>
        </tr>
        <tr>
          <td id="file-get_token-py-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-get_token-py-LC15" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">if</span> <span class="pl-c1">not</span> <span class="pl-s1">item</span>:</td>
        </tr>
        <tr>
          <td id="file-get_token-py-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-get_token-py-LC16" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">return</span> <span class="pl-c1">None</span></td>
        </tr>
        <tr>
          <td id="file-get_token-py-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-get_token-py-LC17" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-get_token-py-L18" class="blob-num js-line-number js-blob-rnum" data-line-number="18"></td>
          <td id="file-get_token-py-LC18" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Check when the token expires</span></td>
        </tr>
        <tr>
          <td id="file-get_token-py-L19" class="blob-num js-line-number js-blob-rnum" data-line-number="19"></td>
          <td id="file-get_token-py-LC19" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">expires_at</span> <span class="pl-c1">=</span> <span class="pl-en">int</span>(<span class="pl-s1">item</span>[<span class="pl-s">"expiresAt"</span>][<span class="pl-s">"N"</span>])</td>
        </tr>
        <tr>
          <td id="file-get_token-py-L20" class="blob-num js-line-number js-blob-rnum" data-line-number="20"></td>
          <td id="file-get_token-py-LC20" class="blob-code blob-code-inner js-file-line">    </td>
        </tr>
        <tr>
          <td id="file-get_token-py-L21" class="blob-num js-line-number js-blob-rnum" data-line-number="21"></td>
          <td id="file-get_token-py-LC21" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># If token expires less than 30 seconds from now, return None</span></td>
        </tr>
        <tr>
          <td id="file-get_token-py-L22" class="blob-num js-line-number js-blob-rnum" data-line-number="22"></td>
          <td id="file-get_token-py-LC22" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">if</span> <span class="pl-s1">expires_at</span> <span class="pl-c1">-</span> <span class="pl-en">int</span>(<span class="pl-s1">time</span>.<span class="pl-c1">time</span>()) <span class="pl-c1">&lt;=</span> <span class="pl-c1">30</span>:</td>
        </tr>
        <tr>
          <td id="file-get_token-py-L23" class="blob-num js-line-number js-blob-rnum" data-line-number="23"></td>
          <td id="file-get_token-py-LC23" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># Debug</span></td>
        </tr>
        <tr>
          <td id="file-get_token-py-L24" class="blob-num js-line-number js-blob-rnum" data-line-number="24"></td>
          <td id="file-get_token-py-LC24" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">if</span> <span class="pl-s1">os</span>.<span class="pl-c1">environ</span>.<span class="pl-c1">get</span>(<span class="pl-s">"VERA_DEBUG"</span>) <span class="pl-c1">==</span> <span class="pl-s">"True"</span>:</td>
        </tr>
        <tr>
          <td id="file-get_token-py-L25" class="blob-num js-line-number js-blob-rnum" data-line-number="25"></td>
          <td id="file-get_token-py-LC25" class="blob-code blob-code-inner js-file-line">            <span class="pl-en">print</span>(<span class="pl-s">"&#128993; Token is expired or expiring in the next 30 seconds, returning None"</span>)</td>
        </tr>
        <tr>
          <td id="file-get_token-py-L26" class="blob-num js-line-number js-blob-rnum" data-line-number="26"></td>
          <td id="file-get_token-py-LC26" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">return</span> <span class="pl-c1">None</span></td>
        </tr>
        <tr>
          <td id="file-get_token-py-L27" class="blob-num js-line-number js-blob-rnum" data-line-number="27"></td>
          <td id="file-get_token-py-LC27" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-get_token-py-L28" class="blob-num js-line-number js-blob-rnum" data-line-number="28"></td>
          <td id="file-get_token-py-LC28" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Store the token in a variable</span></td>
        </tr>
        <tr>
          <td id="file-get_token-py-L29" class="blob-num js-line-number js-blob-rnum" data-line-number="29"></td>
          <td id="file-get_token-py-LC29" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">token</span> <span class="pl-c1">=</span> <span class="pl-s1">item</span>[<span class="pl-s">"accessToken"</span>][<span class="pl-s">"S"</span>]</td>
        </tr>
        <tr>
          <td id="file-get_token-py-L30" class="blob-num js-line-number js-blob-rnum" data-line-number="30"></td>
          <td id="file-get_token-py-LC30" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-get_token-py-L31" class="blob-num js-line-number js-blob-rnum" data-line-number="31"></td>
          <td id="file-get_token-py-LC31" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">return</span> <span class="pl-s1">token</span></td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/4383baae916f221cb40453de749bf397/raw/490d3ca11970460ce86bc3fd80fbd2e888ed09f9/get_token.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/4383baae916f221cb40453de749bf397#file-get_token-py" class="Link--inTextBlock">
          get_token.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><h2>We Found a Token - Awesome</h2><p>Lets jump back to our handle_teams_event function. We&#8217;ve now either validated a token doesn&#8217;t exist ($encrypted_token == None) or it does ($encrypted_token == valid token). </p><p>We first run the logic of if there is a valid token (line 6), then we need to kick off the Worker to do the AI magic, so we register a lambda client on line 8. </p><p>Then on line 12, we add a key of &#8220;token&#8221; set to the encrypted token. </p><p>On line 15, we invoke the worker lambda - we look up the name of the Worker as an environment var injected by terraform. </p><p>Then on line 23 we return, closing out the run. </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist139245781\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-handle_teams_event2-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;handle_teams_event2.py content, created by KyMidd on 02:29AM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;8\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;handle_teams_event2.py\&quot;>\n        <tr>\n          <td id=\&quot;file-handle_teams_event2-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-handle_teams_event2-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-en>handle_teams_event</span>(<span class=pl-s1>body</span>, <span class=pl-s1>event</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event2-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-handle_teams_event2-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># ...</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event2-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-handle_teams_event2-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>encrypted_token</span> <span class=pl-c1>=</span> <span class=pl-en>get_token</span>(<span class=pl-s1>dynamodb_client</span>, <span class=pl-s1>aadObjectId</span>, <span class=pl-s1>token_table_arn</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event2-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-handle_teams_event2-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event2-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-handle_teams_event2-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># If unexpired token is found, send it to the processor lambda</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event2-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-handle_teams_event2-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>if</span> <span class=pl-s1>encrypted_token</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event2-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-handle_teams_event2-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>          <span class=pl-c># Initialize AWS Lambda client</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event2-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-handle_teams_event2-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>          <span class=pl-s1>lambda_client</span> <span class=pl-c1>=</span> <span class=pl-s1>boto3</span>.<span class=pl-c1>client</span>(<span class=pl-s>&amp;#39;lambda&amp;#39;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event2-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-handle_teams_event2-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>          </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event2-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-handle_teams_event2-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>          <span class=pl-c># Prepare the event to send to the processor lambda</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event2-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-handle_teams_event2-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>          <span class=pl-c># Add the token to the event</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event2-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-handle_teams_event2-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>          <span class=pl-s1>event</span>[<span class=pl-s>&amp;quot;token&amp;quot;</span>] <span class=pl-c1>=</span> <span class=pl-s1>encrypted_token</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event2-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-handle_teams_event2-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event2-py-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-handle_teams_event2-py-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>          <span class=pl-c># Asynchronously invoke the processor Lambda</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event2-py-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-handle_teams_event2-py-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>          <span class=pl-s1>lambda_client</span>.<span class=pl-c1>invoke</span>(</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event2-py-L16\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;16\&quot;></td>\n          <td id=\&quot;file-handle_teams_event2-py-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>              <span class=pl-v>FunctionName</span><span class=pl-c1>=</span><span class=pl-s1>os</span>.<span class=pl-c1>environ</span>[<span class=pl-s>&amp;#39;WORKER_LAMBDA_NAME&amp;#39;</span>],</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event2-py-L17\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;17\&quot;></td>\n          <td id=\&quot;file-handle_teams_event2-py-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>              <span class=pl-v>InvocationType</span><span class=pl-c1>=</span><span class=pl-s>&amp;#39;Event&amp;#39;</span>,  <span class=pl-c># Async invocation</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event2-py-L18\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;18\&quot;></td>\n          <td id=\&quot;file-handle_teams_event2-py-LC18\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>              <span class=pl-v>Payload</span><span class=pl-c1>=</span><span class=pl-s1>json</span>.<span class=pl-c1>dumps</span>(<span class=pl-s1>event</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event2-py-L19\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;19\&quot;></td>\n          <td id=\&quot;file-handle_teams_event2-py-LC19\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>          )</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event2-py-L20\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;20\&quot;></td>\n          <td id=\&quot;file-handle_teams_event2-py-LC20\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>          </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event2-py-L21\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;21\&quot;></td>\n          <td id=\&quot;file-handle_teams_event2-py-LC21\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>          <span class=pl-c># Return</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event2-py-L22\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;22\&quot;></td>\n          <td id=\&quot;file-handle_teams_event2-py-LC22\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>          <span class=pl-en>print</span>(<span class=pl-s>&amp;quot;&#128994; Successfully invoked the processor Lambda with the access token&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event2-py-L23\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;23\&quot;></td>\n          <td id=\&quot;file-handle_teams_event2-py-LC23\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>          <span class=pl-k>return</span></td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/285f0cdbe1266f95ec2fa396aa8aed77/raw/aa39c02b7ea01de05268f09bac6ce8737d42f81f/handle_teams_event2.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/285f0cdbe1266f95ec2fa396aa8aed77#file-handle_teams_event2-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          handle_teams_event2.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-110350e56c50.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-110350e56c50.css"><div id="gist139245781" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-handle_teams_event2-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="8" data-paste-markdown-skip="" data-tagsearch-path="handle_teams_event2.py">
        <tbody><tr>
          <td id="file-handle_teams_event2-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-handle_teams_event2-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-en">handle_teams_event</span>(<span class="pl-s1">body</span>, <span class="pl-s1">event</span>):</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event2-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-handle_teams_event2-py-LC2" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># ...</span></td>
        </tr>
        <tr>
          <td id="file-handle_teams_event2-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-handle_teams_event2-py-LC3" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">encrypted_token</span> <span class="pl-c1">=</span> <span class="pl-en">get_token</span>(<span class="pl-s1">dynamodb_client</span>, <span class="pl-s1">aadObjectId</span>, <span class="pl-s1">token_table_arn</span>)</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event2-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-handle_teams_event2-py-LC4" class="blob-code blob-code-inner js-file-line">        </td>
        </tr>
        <tr>
          <td id="file-handle_teams_event2-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-handle_teams_event2-py-LC5" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># If unexpired token is found, send it to the processor lambda</span></td>
        </tr>
        <tr>
          <td id="file-handle_teams_event2-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-handle_teams_event2-py-LC6" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">if</span> <span class="pl-s1">encrypted_token</span>:</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event2-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-handle_teams_event2-py-LC7" class="blob-code blob-code-inner js-file-line">          <span class="pl-c"># Initialize AWS Lambda client</span></td>
        </tr>
        <tr>
          <td id="file-handle_teams_event2-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-handle_teams_event2-py-LC8" class="blob-code blob-code-inner js-file-line">          <span class="pl-s1">lambda_client</span> <span class="pl-c1">=</span> <span class="pl-s1">boto3</span>.<span class="pl-c1">client</span>(<span class="pl-s">'lambda'</span>)</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event2-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-handle_teams_event2-py-LC9" class="blob-code blob-code-inner js-file-line">          </td>
        </tr>
        <tr>
          <td id="file-handle_teams_event2-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-handle_teams_event2-py-LC10" class="blob-code blob-code-inner js-file-line">          <span class="pl-c"># Prepare the event to send to the processor lambda</span></td>
        </tr>
        <tr>
          <td id="file-handle_teams_event2-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-handle_teams_event2-py-LC11" class="blob-code blob-code-inner js-file-line">          <span class="pl-c"># Add the token to the event</span></td>
        </tr>
        <tr>
          <td id="file-handle_teams_event2-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-handle_teams_event2-py-LC12" class="blob-code blob-code-inner js-file-line">          <span class="pl-s1">event</span>[<span class="pl-s">"token"</span>] <span class="pl-c1">=</span> <span class="pl-s1">encrypted_token</span></td>
        </tr>
        <tr>
          <td id="file-handle_teams_event2-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-handle_teams_event2-py-LC13" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event2-py-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-handle_teams_event2-py-LC14" class="blob-code blob-code-inner js-file-line">          <span class="pl-c"># Asynchronously invoke the processor Lambda</span></td>
        </tr>
        <tr>
          <td id="file-handle_teams_event2-py-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-handle_teams_event2-py-LC15" class="blob-code blob-code-inner js-file-line">          <span class="pl-s1">lambda_client</span>.<span class="pl-c1">invoke</span>(</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event2-py-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-handle_teams_event2-py-LC16" class="blob-code blob-code-inner js-file-line">              <span class="pl-v">FunctionName</span><span class="pl-c1">=</span><span class="pl-s1">os</span>.<span class="pl-c1">environ</span>[<span class="pl-s">'WORKER_LAMBDA_NAME'</span>],</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event2-py-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-handle_teams_event2-py-LC17" class="blob-code blob-code-inner js-file-line">              <span class="pl-v">InvocationType</span><span class="pl-c1">=</span><span class="pl-s">'Event'</span>,  <span class="pl-c"># Async invocation</span></td>
        </tr>
        <tr>
          <td id="file-handle_teams_event2-py-L18" class="blob-num js-line-number js-blob-rnum" data-line-number="18"></td>
          <td id="file-handle_teams_event2-py-LC18" class="blob-code blob-code-inner js-file-line">              <span class="pl-v">Payload</span><span class="pl-c1">=</span><span class="pl-s1">json</span>.<span class="pl-c1">dumps</span>(<span class="pl-s1">event</span>)</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event2-py-L19" class="blob-num js-line-number js-blob-rnum" data-line-number="19"></td>
          <td id="file-handle_teams_event2-py-LC19" class="blob-code blob-code-inner js-file-line">          )</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event2-py-L20" class="blob-num js-line-number js-blob-rnum" data-line-number="20"></td>
          <td id="file-handle_teams_event2-py-LC20" class="blob-code blob-code-inner js-file-line">          </td>
        </tr>
        <tr>
          <td id="file-handle_teams_event2-py-L21" class="blob-num js-line-number js-blob-rnum" data-line-number="21"></td>
          <td id="file-handle_teams_event2-py-LC21" class="blob-code blob-code-inner js-file-line">          <span class="pl-c"># Return</span></td>
        </tr>
        <tr>
          <td id="file-handle_teams_event2-py-L22" class="blob-num js-line-number js-blob-rnum" data-line-number="22"></td>
          <td id="file-handle_teams_event2-py-LC22" class="blob-code blob-code-inner js-file-line">          <span class="pl-en">print</span>(<span class="pl-s">"&#128994; Successfully invoked the processor Lambda with the access token"</span>)</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event2-py-L23" class="blob-num js-line-number js-blob-rnum" data-line-number="23"></td>
          <td id="file-handle_teams_event2-py-LC23" class="blob-code blob-code-inner js-file-line">          <span class="pl-k">return</span></td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/285f0cdbe1266f95ec2fa396aa8aed77/raw/aa39c02b7ea01de05268f09bac6ce8737d42f81f/handle_teams_event2.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/285f0cdbe1266f95ec2fa396aa8aed77#file-handle_teams_event2-py" class="Link--inTextBlock">
          handle_teams_event2.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><h2>No Token - No Problem</h2><p>However, if we didn&#8217;t find a token in the dynamo table, we need to do two things: </p><ol><li><p>Store the Conversation in the conversation dynamoDB table</p></li><li><p>Push an SSO Card to the Teams user. </p></li></ol><p>Lets jump back to the handle_teams_event where we do that. </p><p>We&#8217;re working with dynamo again, so we register a client, line 6. </p><p>Then on line 7, we put the conversation payload into the conversation dynamo table, and set the aadObjectId as the user&#8217;s object ID. This is helpfully a string that&#8217;s present in the OAuth2 token that we&#8217;ll get in a second when the user authenticates, and we can resume their conversation. </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist139245931\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-handle_teams_event3-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;handle_teams_event3.py content, created by KyMidd on 02:39AM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;8\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;handle_teams_event3.py\&quot;>\n        <tr>\n          <td id=\&quot;file-handle_teams_event3-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-handle_teams_event3-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-en>handle_teams_event</span>(<span class=pl-s1>body</span>, <span class=pl-s1>event</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event3-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-handle_teams_event3-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># ...</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event3-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-handle_teams_event3-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>if</span> <span class=pl-s1>event_type</span> <span class=pl-c1>==</span> <span class=pl-s>&amp;#39;message&amp;#39;</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event3-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-handle_teams_event3-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># ...</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event3-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-handle_teams_event3-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># Store the conversation ID in DynamoDB</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event3-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-handle_teams_event3-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>dynamodb_client</span> <span class=pl-c1>=</span> <span class=pl-s1>boto3</span>.<span class=pl-c1>client</span>(<span class=pl-s>&amp;quot;dynamodb&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event3-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-handle_teams_event3-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>dynamodb_client</span>.<span class=pl-c1>put_item</span>(</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event3-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-handle_teams_event3-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-v>TableName</span><span class=pl-c1>=</span><span class=pl-s1>conversation_table_arn</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event3-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-handle_teams_event3-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-v>Item</span><span class=pl-c1>=</span>{</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event3-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-handle_teams_event3-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                <span class=pl-s>&amp;quot;aadObjectId&amp;quot;</span>: {<span class=pl-s>&amp;quot;S&amp;quot;</span>: <span class=pl-s1>aadObjectId</span>},</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event3-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-handle_teams_event3-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                <span class=pl-s>&amp;quot;event&amp;quot;</span>: {<span class=pl-s>&amp;quot;S&amp;quot;</span>: <span class=pl-s1>json</span>.<span class=pl-c1>dumps</span>(<span class=pl-s1>event</span>)}, <span class=pl-c># Store the entire event</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event3-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-handle_teams_event3-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            }</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event3-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-handle_teams_event3-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        )</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/b41f894b0a7f58eec4720109072dfff6/raw/b7fbcbada69c53d3f8c139eaced52b7be82cd5da/handle_teams_event3.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/b41f894b0a7f58eec4720109072dfff6#file-handle_teams_event3-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          handle_teams_event3.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-110350e56c50.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-110350e56c50.css"><div id="gist139245931" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-handle_teams_event3-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="8" data-paste-markdown-skip="" data-tagsearch-path="handle_teams_event3.py">
        <tbody><tr>
          <td id="file-handle_teams_event3-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-handle_teams_event3-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-en">handle_teams_event</span>(<span class="pl-s1">body</span>, <span class="pl-s1">event</span>):</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event3-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-handle_teams_event3-py-LC2" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># ...</span></td>
        </tr>
        <tr>
          <td id="file-handle_teams_event3-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-handle_teams_event3-py-LC3" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">if</span> <span class="pl-s1">event_type</span> <span class="pl-c1">==</span> <span class="pl-s">'message'</span>:</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event3-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-handle_teams_event3-py-LC4" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># ...</span></td>
        </tr>
        <tr>
          <td id="file-handle_teams_event3-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-handle_teams_event3-py-LC5" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># Store the conversation ID in DynamoDB</span></td>
        </tr>
        <tr>
          <td id="file-handle_teams_event3-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-handle_teams_event3-py-LC6" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">dynamodb_client</span> <span class="pl-c1">=</span> <span class="pl-s1">boto3</span>.<span class="pl-c1">client</span>(<span class="pl-s">"dynamodb"</span>)</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event3-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-handle_teams_event3-py-LC7" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">dynamodb_client</span>.<span class="pl-c1">put_item</span>(</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event3-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-handle_teams_event3-py-LC8" class="blob-code blob-code-inner js-file-line">            <span class="pl-v">TableName</span><span class="pl-c1">=</span><span class="pl-s1">conversation_table_arn</span>,</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event3-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-handle_teams_event3-py-LC9" class="blob-code blob-code-inner js-file-line">            <span class="pl-v">Item</span><span class="pl-c1">=</span>{</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event3-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-handle_teams_event3-py-LC10" class="blob-code blob-code-inner js-file-line">                <span class="pl-s">"aadObjectId"</span>: {<span class="pl-s">"S"</span>: <span class="pl-s1">aadObjectId</span>},</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event3-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-handle_teams_event3-py-LC11" class="blob-code blob-code-inner js-file-line">                <span class="pl-s">"event"</span>: {<span class="pl-s">"S"</span>: <span class="pl-s1">json</span>.<span class="pl-c1">dumps</span>(<span class="pl-s1">event</span>)}, <span class="pl-c"># Store the entire event</span></td>
        </tr>
        <tr>
          <td id="file-handle_teams_event3-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-handle_teams_event3-py-LC12" class="blob-code blob-code-inner js-file-line">            }</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event3-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-handle_teams_event3-py-LC13" class="blob-code blob-code-inner js-file-line">        )</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/b41f894b0a7f58eec4720109072dfff6/raw/b7fbcbada69c53d3f8c139eaced52b7be82cd5da/handle_teams_event3.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/b41f894b0a7f58eec4720109072dfff6#file-handle_teams_event3-py" class="Link--inTextBlock">
          handle_teams_event3.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>Then we need to build the SSO Card, but we need some information to do that. </p><p>We&#8217;ll grab our secrets from secrets manager using the get_secret_ssm_layer function - this is the same as I&#8217;ve covered previously in the AWS series, so I won&#8217;t cover it here. We use the lambda secrets manager AWS layer (it&#8217;s the fastest method!)</p><p>Then we load the secrets as json and disambiguate the different variables, line 9-12. </p><p>Then we get the bot bearer token that we use to send messages. We&#8217;ll use this to send the SSO Card in a second. </p><p>Lets dig into that. </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist139245967\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-handle_teams_event4-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;handle_teams_event4.py content, created by KyMidd on 02:43AM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;8\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;handle_teams_event4.py\&quot;>\n        <tr>\n          <td id=\&quot;file-handle_teams_event4-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-handle_teams_event4-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-en>handle_teams_event</span>(<span class=pl-s1>body</span>, <span class=pl-s1>event</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event4-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-handle_teams_event4-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># ...</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event4-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-handle_teams_event4-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>if</span> <span class=pl-s1>event_type</span> <span class=pl-c1>==</span> <span class=pl-s>&amp;#39;message&amp;#39;</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event4-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-handle_teams_event4-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># ...</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event4-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-handle_teams_event4-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># Fetch secret package</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event4-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-handle_teams_event4-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>secrets</span> <span class=pl-c1>=</span> <span class=pl-en>get_secret_ssm_layer</span>(<span class=pl-s1>bot_secret_name</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event4-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-handle_teams_event4-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event4-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-handle_teams_event4-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># Disambiguate secrets</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event4-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-handle_teams_event4-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>secrets_json</span> <span class=pl-c1>=</span> <span class=pl-s1>json</span>.<span class=pl-c1>loads</span>(<span class=pl-s1>secrets</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event4-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-handle_teams_event4-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c1>TENANT_ID</span> <span class=pl-c1>=</span> <span class=pl-s1>secrets_json</span>[<span class=pl-s>&amp;quot;TENANT_ID&amp;quot;</span>]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event4-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-handle_teams_event4-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c1>CLIENT_ID</span> <span class=pl-c1>=</span> <span class=pl-s1>secrets_json</span>[<span class=pl-s>&amp;quot;CLIENT_ID&amp;quot;</span>]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event4-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-handle_teams_event4-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c1>CLIENT_SECRET</span> <span class=pl-c1>=</span> <span class=pl-s1>secrets_json</span>[<span class=pl-s>&amp;quot;CLIENT_SECRET&amp;quot;</span>]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event4-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-handle_teams_event4-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event4-py-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-handle_teams_event4-py-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># Now we can use the bot token and signing secret</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event4-py-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-handle_teams_event4-py-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-en>print</span>(<span class=pl-s>&amp;quot;&#128994; Successfully retrieved secrets from AWS Secrets Manager&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event4-py-L16\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;16\&quot;></td>\n          <td id=\&quot;file-handle_teams_event4-py-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event4-py-L17\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;17\&quot;></td>\n          <td id=\&quot;file-handle_teams_event4-py-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># Get bearer token for the bot to use to post messages</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event4-py-L18\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;18\&quot;></td>\n          <td id=\&quot;file-handle_teams_event4-py-LC18\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>bot_bearer_token</span> <span class=pl-c1>=</span> <span class=pl-en>get_teams_bearer_token</span>(<span class=pl-c1>TENANT_ID</span>, <span class=pl-c1>CLIENT_ID</span>, <span class=pl-c1>CLIENT_SECRET</span>)</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/688afd52cf10b0f83f208d93a9a44de6/raw/f380faa6c22e96a25643fd9e41fea40ec5ff2395/handle_teams_event4.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/688afd52cf10b0f83f208d93a9a44de6#file-handle_teams_event4-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          handle_teams_event4.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-110350e56c50.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-110350e56c50.css"><div id="gist139245967" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-handle_teams_event4-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="8" data-paste-markdown-skip="" data-tagsearch-path="handle_teams_event4.py">
        <tbody><tr>
          <td id="file-handle_teams_event4-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-handle_teams_event4-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-en">handle_teams_event</span>(<span class="pl-s1">body</span>, <span class="pl-s1">event</span>):</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event4-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-handle_teams_event4-py-LC2" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># ...</span></td>
        </tr>
        <tr>
          <td id="file-handle_teams_event4-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-handle_teams_event4-py-LC3" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">if</span> <span class="pl-s1">event_type</span> <span class="pl-c1">==</span> <span class="pl-s">'message'</span>:</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event4-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-handle_teams_event4-py-LC4" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># ...</span></td>
        </tr>
        <tr>
          <td id="file-handle_teams_event4-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-handle_teams_event4-py-LC5" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># Fetch secret package</span></td>
        </tr>
        <tr>
          <td id="file-handle_teams_event4-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-handle_teams_event4-py-LC6" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">secrets</span> <span class="pl-c1">=</span> <span class="pl-en">get_secret_ssm_layer</span>(<span class="pl-s1">bot_secret_name</span>)</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event4-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-handle_teams_event4-py-LC7" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event4-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-handle_teams_event4-py-LC8" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># Disambiguate secrets</span></td>
        </tr>
        <tr>
          <td id="file-handle_teams_event4-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-handle_teams_event4-py-LC9" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">secrets_json</span> <span class="pl-c1">=</span> <span class="pl-s1">json</span>.<span class="pl-c1">loads</span>(<span class="pl-s1">secrets</span>)</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event4-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-handle_teams_event4-py-LC10" class="blob-code blob-code-inner js-file-line">        <span class="pl-c1">TENANT_ID</span> <span class="pl-c1">=</span> <span class="pl-s1">secrets_json</span>[<span class="pl-s">"TENANT_ID"</span>]</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event4-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-handle_teams_event4-py-LC11" class="blob-code blob-code-inner js-file-line">        <span class="pl-c1">CLIENT_ID</span> <span class="pl-c1">=</span> <span class="pl-s1">secrets_json</span>[<span class="pl-s">"CLIENT_ID"</span>]</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event4-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-handle_teams_event4-py-LC12" class="blob-code blob-code-inner js-file-line">        <span class="pl-c1">CLIENT_SECRET</span> <span class="pl-c1">=</span> <span class="pl-s1">secrets_json</span>[<span class="pl-s">"CLIENT_SECRET"</span>]</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event4-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-handle_teams_event4-py-LC13" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event4-py-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-handle_teams_event4-py-LC14" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># Now we can use the bot token and signing secret</span></td>
        </tr>
        <tr>
          <td id="file-handle_teams_event4-py-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-handle_teams_event4-py-LC15" class="blob-code blob-code-inner js-file-line">        <span class="pl-en">print</span>(<span class="pl-s">"&#128994; Successfully retrieved secrets from AWS Secrets Manager"</span>)</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event4-py-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-handle_teams_event4-py-LC16" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event4-py-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-handle_teams_event4-py-LC17" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># Get bearer token for the bot to use to post messages</span></td>
        </tr>
        <tr>
          <td id="file-handle_teams_event4-py-L18" class="blob-num js-line-number js-blob-rnum" data-line-number="18"></td>
          <td id="file-handle_teams_event4-py-LC18" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">bot_bearer_token</span> <span class="pl-c1">=</span> <span class="pl-en">get_teams_bearer_token</span>(<span class="pl-c1">TENANT_ID</span>, <span class="pl-c1">CLIENT_ID</span>, <span class="pl-c1">CLIENT_SECRET</span>)</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/688afd52cf10b0f83f208d93a9a44de6/raw/f380faa6c22e96a25643fd9e41fea40ec5ff2395/handle_teams_event4.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/688afd52cf10b0f83f208d93a9a44de6#file-handle_teams_event4-py" class="Link--inTextBlock">
          handle_teams_event4.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>This function grabs us a bot bearer token - this lets us post messages to Teams as our bot user using the BotFramework. </p><p>We set our token URL on line 4.</p><blockquote><p>Expert tip - this is different for a &#8220;global&#8221; Bot vs a &#8220;regional&#8221; Bot. Generally, you should be building a Global bot - &#8220;regional&#8221; is an old standard that isn&#8217;t used much anymore. </p></blockquote><p>Then on line 7 we set the scope - the permissions we want. The &#8220;.default&#8221; scope grabs all permissions available. In some use cases you&#8217;d prune permissions based on user or workload, but this Bot is only used for this workload, so we want all the permissions. </p><p>Then on line 10 we build the payload - our client ID and secret (fetched from secrets manager) and our scope (permissions to ask for). </p><p>We use the requests library to send it, line 18, and check the status response on line 19. If it fails, we throw the error. </p><p>On line 22, we extract our access_token - a token we&#8217;ll use to send messages, and then return it on line 24. </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist139245999\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-get_teams_bearer_token-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;get_teams_bearer_token.py content, created by KyMidd on 02:47AM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;8\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;get_teams_bearer_token.py\&quot;>\n        <tr>\n          <td id=\&quot;file-get_teams_bearer_token-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-get_teams_bearer_token-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-en>get_teams_bearer_token</span>(<span class=pl-c1>TENANT_ID</span>, <span class=pl-c1>CLIENT_ID</span>, <span class=pl-c1>CLIENT_SECRET</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_bearer_token-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-get_teams_bearer_token-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_bearer_token-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-get_teams_bearer_token-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Token endpoint for Azure AD - multi tenant</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_bearer_token-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-get_teams_bearer_token-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>token_url</span> <span class=pl-c1>=</span> <span class=pl-s>f&amp;quot;https://login.microsoftonline.com/botframework.com/oauth2/v2.0/token&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_bearer_token-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-get_teams_bearer_token-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_bearer_token-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-get_teams_bearer_token-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Bot Framework requires this scope</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_bearer_token-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-get_teams_bearer_token-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>scope</span> <span class=pl-c1>=</span> <span class=pl-s>&amp;quot;https://api.botframework.com/.default&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_bearer_token-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-get_teams_bearer_token-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_bearer_token-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-get_teams_bearer_token-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Build the request</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_bearer_token-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-get_teams_bearer_token-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>payload</span> <span class=pl-c1>=</span> {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_bearer_token-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-get_teams_bearer_token-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s>&amp;quot;grant_type&amp;quot;</span>: <span class=pl-s>&amp;quot;client_credentials&amp;quot;</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_bearer_token-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-get_teams_bearer_token-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s>&amp;quot;client_id&amp;quot;</span>: <span class=pl-c1>CLIENT_ID</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_bearer_token-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-get_teams_bearer_token-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s>&amp;quot;client_secret&amp;quot;</span>: <span class=pl-c1>CLIENT_SECRET</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_bearer_token-py-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-get_teams_bearer_token-py-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s>&amp;quot;scope&amp;quot;</span>: <span class=pl-s1>scope</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_bearer_token-py-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-get_teams_bearer_token-py-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    }</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_bearer_token-py-L16\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;16\&quot;></td>\n          <td id=\&quot;file-get_teams_bearer_token-py-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_bearer_token-py-L17\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;17\&quot;></td>\n          <td id=\&quot;file-get_teams_bearer_token-py-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Request the token</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_bearer_token-py-L18\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;18\&quot;></td>\n          <td id=\&quot;file-get_teams_bearer_token-py-LC18\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>response</span> <span class=pl-c1>=</span> <span class=pl-s1>requests</span>.<span class=pl-c1>post</span>(<span class=pl-s1>token_url</span>, <span class=pl-s1>data</span><span class=pl-c1>=</span><span class=pl-s1>payload</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_bearer_token-py-L19\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;19\&quot;></td>\n          <td id=\&quot;file-get_teams_bearer_token-py-LC19\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>response</span>.<span class=pl-c1>raise_for_status</span>()  <span class=pl-c># This will throw an error if the request fails</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_bearer_token-py-L20\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;20\&quot;></td>\n          <td id=\&quot;file-get_teams_bearer_token-py-LC20\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_bearer_token-py-L21\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;21\&quot;></td>\n          <td id=\&quot;file-get_teams_bearer_token-py-LC21\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Extract the token</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_bearer_token-py-L22\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;22\&quot;></td>\n          <td id=\&quot;file-get_teams_bearer_token-py-LC22\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>bearer_token</span> <span class=pl-c1>=</span> <span class=pl-s1>response</span>.<span class=pl-c1>json</span>()[<span class=pl-s>&amp;quot;access_token&amp;quot;</span>]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_bearer_token-py-L23\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;23\&quot;></td>\n          <td id=\&quot;file-get_teams_bearer_token-py-LC23\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-get_teams_bearer_token-py-L24\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;24\&quot;></td>\n          <td id=\&quot;file-get_teams_bearer_token-py-LC24\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>return</span> <span class=pl-s1>bearer_token</span></td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/5fd56a65a253e684087b2e0860c09650/raw/92177a94aad4802931e8e6ca388a7b477d9db521/get_teams_bearer_token.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/5fd56a65a253e684087b2e0860c09650#file-get_teams_bearer_token-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          get_teams_bearer_token.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-110350e56c50.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-110350e56c50.css"><div id="gist139245999" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-get_teams_bearer_token-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="8" data-paste-markdown-skip="" data-tagsearch-path="get_teams_bearer_token.py">
        <tbody><tr>
          <td id="file-get_teams_bearer_token-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-get_teams_bearer_token-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-en">get_teams_bearer_token</span>(<span class="pl-c1">TENANT_ID</span>, <span class="pl-c1">CLIENT_ID</span>, <span class="pl-c1">CLIENT_SECRET</span>):</td>
        </tr>
        <tr>
          <td id="file-get_teams_bearer_token-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-get_teams_bearer_token-py-LC2" class="blob-code blob-code-inner js-file-line">  </td>
        </tr>
        <tr>
          <td id="file-get_teams_bearer_token-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-get_teams_bearer_token-py-LC3" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Token endpoint for Azure AD - multi tenant</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_bearer_token-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-get_teams_bearer_token-py-LC4" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">token_url</span> <span class="pl-c1">=</span> <span class="pl-s">f"https://login.microsoftonline.com/botframework.com/oauth2/v2.0/token"</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_bearer_token-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-get_teams_bearer_token-py-LC5" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-get_teams_bearer_token-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-get_teams_bearer_token-py-LC6" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Bot Framework requires this scope</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_bearer_token-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-get_teams_bearer_token-py-LC7" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">scope</span> <span class="pl-c1">=</span> <span class="pl-s">"https://api.botframework.com/.default"</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_bearer_token-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-get_teams_bearer_token-py-LC8" class="blob-code blob-code-inner js-file-line">    </td>
        </tr>
        <tr>
          <td id="file-get_teams_bearer_token-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-get_teams_bearer_token-py-LC9" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Build the request</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_bearer_token-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-get_teams_bearer_token-py-LC10" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">payload</span> <span class="pl-c1">=</span> {</td>
        </tr>
        <tr>
          <td id="file-get_teams_bearer_token-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-get_teams_bearer_token-py-LC11" class="blob-code blob-code-inner js-file-line">        <span class="pl-s">"grant_type"</span>: <span class="pl-s">"client_credentials"</span>,</td>
        </tr>
        <tr>
          <td id="file-get_teams_bearer_token-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-get_teams_bearer_token-py-LC12" class="blob-code blob-code-inner js-file-line">        <span class="pl-s">"client_id"</span>: <span class="pl-c1">CLIENT_ID</span>,</td>
        </tr>
        <tr>
          <td id="file-get_teams_bearer_token-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-get_teams_bearer_token-py-LC13" class="blob-code blob-code-inner js-file-line">        <span class="pl-s">"client_secret"</span>: <span class="pl-c1">CLIENT_SECRET</span>,</td>
        </tr>
        <tr>
          <td id="file-get_teams_bearer_token-py-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-get_teams_bearer_token-py-LC14" class="blob-code blob-code-inner js-file-line">        <span class="pl-s">"scope"</span>: <span class="pl-s1">scope</span>,</td>
        </tr>
        <tr>
          <td id="file-get_teams_bearer_token-py-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-get_teams_bearer_token-py-LC15" class="blob-code blob-code-inner js-file-line">    }</td>
        </tr>
        <tr>
          <td id="file-get_teams_bearer_token-py-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-get_teams_bearer_token-py-LC16" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-get_teams_bearer_token-py-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-get_teams_bearer_token-py-LC17" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Request the token</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_bearer_token-py-L18" class="blob-num js-line-number js-blob-rnum" data-line-number="18"></td>
          <td id="file-get_teams_bearer_token-py-LC18" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">response</span> <span class="pl-c1">=</span> <span class="pl-s1">requests</span>.<span class="pl-c1">post</span>(<span class="pl-s1">token_url</span>, <span class="pl-s1">data</span><span class="pl-c1">=</span><span class="pl-s1">payload</span>)</td>
        </tr>
        <tr>
          <td id="file-get_teams_bearer_token-py-L19" class="blob-num js-line-number js-blob-rnum" data-line-number="19"></td>
          <td id="file-get_teams_bearer_token-py-LC19" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">response</span>.<span class="pl-c1">raise_for_status</span>()  <span class="pl-c"># This will throw an error if the request fails</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_bearer_token-py-L20" class="blob-num js-line-number js-blob-rnum" data-line-number="20"></td>
          <td id="file-get_teams_bearer_token-py-LC20" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-get_teams_bearer_token-py-L21" class="blob-num js-line-number js-blob-rnum" data-line-number="21"></td>
          <td id="file-get_teams_bearer_token-py-LC21" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Extract the token</span></td>
        </tr>
        <tr>
          <td id="file-get_teams_bearer_token-py-L22" class="blob-num js-line-number js-blob-rnum" data-line-number="22"></td>
          <td id="file-get_teams_bearer_token-py-LC22" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">bearer_token</span> <span class="pl-c1">=</span> <span class="pl-s1">response</span>.<span class="pl-c1">json</span>()[<span class="pl-s">"access_token"</span>]</td>
        </tr>
        <tr>
          <td id="file-get_teams_bearer_token-py-L23" class="blob-num js-line-number js-blob-rnum" data-line-number="23"></td>
          <td id="file-get_teams_bearer_token-py-LC23" class="blob-code blob-code-inner js-file-line">    </td>
        </tr>
        <tr>
          <td id="file-get_teams_bearer_token-py-L24" class="blob-num js-line-number js-blob-rnum" data-line-number="24"></td>
          <td id="file-get_teams_bearer_token-py-LC24" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">return</span> <span class="pl-s1">bearer_token</span></td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/5fd56a65a253e684087b2e0860c09650/raw/92177a94aad4802931e8e6ca388a7b477d9db521/get_teams_bearer_token.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/5fd56a65a253e684087b2e0860c09650#file-get_teams_bearer_token-py" class="Link--inTextBlock">
          get_teams_bearer_token.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><h2>Build an IdP OAuth2 Redirect URL</h2><p>Next we&#8217;ll build the auth URL - I built a function just for this because it&#8217;s so complex. Let&#8217;s zoom in on that. </p><p>On line 6 we set the scope of permissions we want (.default), which is all of them. </p><p>Then we call the function to build our OAuth URL. </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist139246047\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-handle_teams_event5-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;handle_teams_event5.py content, created by KyMidd on 02:51AM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;8\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;handle_teams_event5.py\&quot;>\n        <tr>\n          <td id=\&quot;file-handle_teams_event5-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-handle_teams_event5-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-en>handle_teams_event</span>(<span class=pl-s1>body</span>, <span class=pl-s1>event</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event5-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-handle_teams_event5-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># ...</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event5-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-handle_teams_event5-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>if</span> <span class=pl-s1>event_type</span> <span class=pl-c1>==</span> <span class=pl-s>&amp;#39;message&amp;#39;</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event5-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-handle_teams_event5-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># ...</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event5-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-handle_teams_event5-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># Build oauth url</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event5-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-handle_teams_event5-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>scope</span> <span class=pl-c1>=</span> <span class=pl-s>&amp;quot;https://graph.microsoft.com/.default&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event5-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-handle_teams_event5-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>auth_url</span> <span class=pl-c1>=</span> <span class=pl-en>build_oauth_url</span>(<span class=pl-c1>TENANT_ID</span>, <span class=pl-c1>CLIENT_ID</span>, <span class=pl-s1>aadObjectId</span>, <span class=pl-s1>scope</span>)</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/e1f55e489e63663d2f0755c5d626ab87/raw/f6c7a56b71171042adcd7e291901a3c00cebbb28/handle_teams_event5.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/e1f55e489e63663d2f0755c5d626ab87#file-handle_teams_event5-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          handle_teams_event5.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-110350e56c50.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-110350e56c50.css"><div id="gist139246047" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-handle_teams_event5-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="8" data-paste-markdown-skip="" data-tagsearch-path="handle_teams_event5.py">
        <tbody><tr>
          <td id="file-handle_teams_event5-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-handle_teams_event5-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-en">handle_teams_event</span>(<span class="pl-s1">body</span>, <span class="pl-s1">event</span>):</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event5-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-handle_teams_event5-py-LC2" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># ...</span></td>
        </tr>
        <tr>
          <td id="file-handle_teams_event5-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-handle_teams_event5-py-LC3" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">if</span> <span class="pl-s1">event_type</span> <span class="pl-c1">==</span> <span class="pl-s">'message'</span>:</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event5-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-handle_teams_event5-py-LC4" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># ...</span></td>
        </tr>
        <tr>
          <td id="file-handle_teams_event5-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-handle_teams_event5-py-LC5" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># Build oauth url</span></td>
        </tr>
        <tr>
          <td id="file-handle_teams_event5-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-handle_teams_event5-py-LC6" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">scope</span> <span class="pl-c1">=</span> <span class="pl-s">"https://graph.microsoft.com/.default"</span></td>
        </tr>
        <tr>
          <td id="file-handle_teams_event5-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-handle_teams_event5-py-LC7" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">auth_url</span> <span class="pl-c1">=</span> <span class="pl-en">build_oauth_url</span>(<span class="pl-c1">TENANT_ID</span>, <span class="pl-c1">CLIENT_ID</span>, <span class="pl-s1">aadObjectId</span>, <span class="pl-s1">scope</span>)</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/e1f55e489e63663d2f0755c5d626ab87/raw/f6c7a56b71171042adcd7e291901a3c00cebbb28/handle_teams_event5.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/e1f55e489e63663d2f0755c5d626ab87#file-handle_teams_event5-py" class="Link--inTextBlock">
          handle_teams_event5.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>We need to build an OAuth URL. That&#8217;s the VERY long URL that we&#8217;ll send to the user as a clickable button in the SSO Card. It&#8217;ll take them to the Microsoft Entra SSO (or whatever IdP you use). It needs a lot of very precise information. </p><p>On line 2, we start with a base URL - we need users to authenticate to Entra in our tenant, so we build that URL. </p><p>The URI is &#8220;/callback&#8221; - that&#8217;s our top-level URI indicator that the OAuth token is sent to our lambda. </p><p>Next up, we want to define a map of our query parameters - these are information that are going to be embedded in our URL: </p><ul><li><p>The client ID of our caller</p></li><li><p>The response_type of &#8220;code&#8221;</p></li><li><p>The redirect URI - to tell the IdP (Entra) where to send the OAuth2 token</p></li><li><p>The scope/permissions we want (again, .default for all permissions, this time for the Graph API)</p></li><li><p>The &#8220;state&#8221; which is the user&#8217;s aad_object_id</p></li></ul><p>Then we encode the whole thing in valid http using the urlencode() library, line 17, and return it, line 23. </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist139246058\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-build_oauth_url-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;build_oauth_url.py content, created by KyMidd on 02:52AM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;8\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;build_oauth_url.py\&quot;>\n        <tr>\n          <td id=\&quot;file-build_oauth_url-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-build_oauth_url-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-en>build_oauth_url</span>(<span class=pl-s1>tenant_id</span>, <span class=pl-s1>client_id</span>, <span class=pl-s1>aad_object_id</span>, <span class=pl-s1>scope</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_oauth_url-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-build_oauth_url-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>base_url</span> <span class=pl-c1>=</span> <span class=pl-s>f&amp;quot;https://login.microsoftonline.com/<span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>tenant_id</span><span class=pl-kos>}</span></span>/oauth2/v2.0/authorize&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_oauth_url-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-build_oauth_url-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_oauth_url-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-build_oauth_url-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Build redirect URI</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_oauth_url-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-build_oauth_url-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>redirect_uri</span> <span class=pl-c1>=</span> <span class=pl-s>f&amp;quot;<span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>redirect_function_url</span><span class=pl-kos>}</span></span>/callback&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_oauth_url-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-build_oauth_url-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_oauth_url-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-build_oauth_url-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>query_params</span> <span class=pl-c1>=</span> {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_oauth_url-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-build_oauth_url-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s>&amp;quot;client_id&amp;quot;</span>: <span class=pl-s1>client_id</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_oauth_url-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-build_oauth_url-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s>&amp;quot;response_type&amp;quot;</span>: <span class=pl-s>&amp;quot;code&amp;quot;</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_oauth_url-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-build_oauth_url-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s>&amp;quot;redirect_uri&amp;quot;</span>: <span class=pl-s1>redirect_uri</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_oauth_url-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-build_oauth_url-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s>&amp;quot;response_mode&amp;quot;</span>: <span class=pl-s>&amp;quot;query&amp;quot;</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_oauth_url-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-build_oauth_url-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s>&amp;quot;scope&amp;quot;</span>: <span class=pl-s1>scope</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_oauth_url-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-build_oauth_url-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s>&amp;quot;state&amp;quot;</span>: <span class=pl-s1>aad_object_id</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_oauth_url-py-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-build_oauth_url-py-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    }</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_oauth_url-py-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-build_oauth_url-py-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_oauth_url-py-L16\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;16\&quot;></td>\n          <td id=\&quot;file-build_oauth_url-py-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Encode the query parameters</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_oauth_url-py-L17\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;17\&quot;></td>\n          <td id=\&quot;file-build_oauth_url-py-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>oauth_url</span> <span class=pl-c1>=</span> <span class=pl-s>f&amp;quot;<span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>base_url</span><span class=pl-kos>}</span></span>?<span class=pl-s1><span class=pl-kos>{</span><span class=pl-en>urlencode</span>(<span class=pl-s1>query_params</span>)<span class=pl-kos>}</span></span>&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_oauth_url-py-L18\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;18\&quot;></td>\n          <td id=\&quot;file-build_oauth_url-py-LC18\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_oauth_url-py-L19\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;19\&quot;></td>\n          <td id=\&quot;file-build_oauth_url-py-LC19\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Debug</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_oauth_url-py-L20\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;20\&quot;></td>\n          <td id=\&quot;file-build_oauth_url-py-LC20\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>if</span> <span class=pl-s1>os</span>.<span class=pl-c1>environ</span>.<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;VERA_DEBUG&amp;quot;</span>) <span class=pl-c1>==</span> <span class=pl-s>&amp;quot;True&amp;quot;</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_oauth_url-py-L21\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;21\&quot;></td>\n          <td id=\&quot;file-build_oauth_url-py-LC21\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-en>print</span>(<span class=pl-s>&amp;quot;&#128279; OAuth URL encoded:&amp;quot;</span>, <span class=pl-s1>oauth_url</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_oauth_url-py-L22\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;22\&quot;></td>\n          <td id=\&quot;file-build_oauth_url-py-LC22\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_oauth_url-py-L23\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;23\&quot;></td>\n          <td id=\&quot;file-build_oauth_url-py-LC23\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>return</span> <span class=pl-s1>oauth_url</span></td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/bf206f2bef498f1eb2c16aff439cea2c/raw/129a80c49ca432e44e0b2232f720ef78616bce91/build_oauth_url.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/bf206f2bef498f1eb2c16aff439cea2c#file-build_oauth_url-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          build_oauth_url.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-110350e56c50.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-110350e56c50.css"><div id="gist139246058" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-build_oauth_url-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="8" data-paste-markdown-skip="" data-tagsearch-path="build_oauth_url.py">
        <tbody><tr>
          <td id="file-build_oauth_url-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-build_oauth_url-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-en">build_oauth_url</span>(<span class="pl-s1">tenant_id</span>, <span class="pl-s1">client_id</span>, <span class="pl-s1">aad_object_id</span>, <span class="pl-s1">scope</span>):</td>
        </tr>
        <tr>
          <td id="file-build_oauth_url-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-build_oauth_url-py-LC2" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">base_url</span> <span class="pl-c1">=</span> <span class="pl-s">f"https://login.microsoftonline.com/<span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">tenant_id</span><span class="pl-kos">}</span></span>/oauth2/v2.0/authorize"</span></td>
        </tr>
        <tr>
          <td id="file-build_oauth_url-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-build_oauth_url-py-LC3" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-build_oauth_url-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-build_oauth_url-py-LC4" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Build redirect URI</span></td>
        </tr>
        <tr>
          <td id="file-build_oauth_url-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-build_oauth_url-py-LC5" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">redirect_uri</span> <span class="pl-c1">=</span> <span class="pl-s">f"<span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">redirect_function_url</span><span class="pl-kos">}</span></span>/callback"</span></td>
        </tr>
        <tr>
          <td id="file-build_oauth_url-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-build_oauth_url-py-LC6" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-build_oauth_url-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-build_oauth_url-py-LC7" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">query_params</span> <span class="pl-c1">=</span> {</td>
        </tr>
        <tr>
          <td id="file-build_oauth_url-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-build_oauth_url-py-LC8" class="blob-code blob-code-inner js-file-line">        <span class="pl-s">"client_id"</span>: <span class="pl-s1">client_id</span>,</td>
        </tr>
        <tr>
          <td id="file-build_oauth_url-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-build_oauth_url-py-LC9" class="blob-code blob-code-inner js-file-line">        <span class="pl-s">"response_type"</span>: <span class="pl-s">"code"</span>,</td>
        </tr>
        <tr>
          <td id="file-build_oauth_url-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-build_oauth_url-py-LC10" class="blob-code blob-code-inner js-file-line">        <span class="pl-s">"redirect_uri"</span>: <span class="pl-s1">redirect_uri</span>,</td>
        </tr>
        <tr>
          <td id="file-build_oauth_url-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-build_oauth_url-py-LC11" class="blob-code blob-code-inner js-file-line">        <span class="pl-s">"response_mode"</span>: <span class="pl-s">"query"</span>,</td>
        </tr>
        <tr>
          <td id="file-build_oauth_url-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-build_oauth_url-py-LC12" class="blob-code blob-code-inner js-file-line">        <span class="pl-s">"scope"</span>: <span class="pl-s1">scope</span>,</td>
        </tr>
        <tr>
          <td id="file-build_oauth_url-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-build_oauth_url-py-LC13" class="blob-code blob-code-inner js-file-line">        <span class="pl-s">"state"</span>: <span class="pl-s1">aad_object_id</span></td>
        </tr>
        <tr>
          <td id="file-build_oauth_url-py-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-build_oauth_url-py-LC14" class="blob-code blob-code-inner js-file-line">    }</td>
        </tr>
        <tr>
          <td id="file-build_oauth_url-py-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-build_oauth_url-py-LC15" class="blob-code blob-code-inner js-file-line">    </td>
        </tr>
        <tr>
          <td id="file-build_oauth_url-py-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-build_oauth_url-py-LC16" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Encode the query parameters</span></td>
        </tr>
        <tr>
          <td id="file-build_oauth_url-py-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-build_oauth_url-py-LC17" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">oauth_url</span> <span class="pl-c1">=</span> <span class="pl-s">f"<span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">base_url</span><span class="pl-kos">}</span></span>?<span class="pl-s1"><span class="pl-kos">{</span><span class="pl-en">urlencode</span>(<span class="pl-s1">query_params</span>)<span class="pl-kos">}</span></span>"</span></td>
        </tr>
        <tr>
          <td id="file-build_oauth_url-py-L18" class="blob-num js-line-number js-blob-rnum" data-line-number="18"></td>
          <td id="file-build_oauth_url-py-LC18" class="blob-code blob-code-inner js-file-line">    </td>
        </tr>
        <tr>
          <td id="file-build_oauth_url-py-L19" class="blob-num js-line-number js-blob-rnum" data-line-number="19"></td>
          <td id="file-build_oauth_url-py-LC19" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Debug</span></td>
        </tr>
        <tr>
          <td id="file-build_oauth_url-py-L20" class="blob-num js-line-number js-blob-rnum" data-line-number="20"></td>
          <td id="file-build_oauth_url-py-LC20" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">if</span> <span class="pl-s1">os</span>.<span class="pl-c1">environ</span>.<span class="pl-c1">get</span>(<span class="pl-s">"VERA_DEBUG"</span>) <span class="pl-c1">==</span> <span class="pl-s">"True"</span>:</td>
        </tr>
        <tr>
          <td id="file-build_oauth_url-py-L21" class="blob-num js-line-number js-blob-rnum" data-line-number="21"></td>
          <td id="file-build_oauth_url-py-LC21" class="blob-code blob-code-inner js-file-line">        <span class="pl-en">print</span>(<span class="pl-s">"&#128279; OAuth URL encoded:"</span>, <span class="pl-s1">oauth_url</span>)</td>
        </tr>
        <tr>
          <td id="file-build_oauth_url-py-L22" class="blob-num js-line-number js-blob-rnum" data-line-number="22"></td>
          <td id="file-build_oauth_url-py-LC22" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-build_oauth_url-py-L23" class="blob-num js-line-number js-blob-rnum" data-line-number="23"></td>
          <td id="file-build_oauth_url-py-LC23" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">return</span> <span class="pl-s1">oauth_url</span></td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/bf206f2bef498f1eb2c16aff439cea2c/raw/129a80c49ca432e44e0b2232f720ef78616bce91/build_oauth_url.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/bf206f2bef498f1eb2c16aff439cea2c#file-build_oauth_url-py" class="Link--inTextBlock">
          build_oauth_url.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>Back to our handle_teams_event function - now that we have the OAuth2 URL, we need to build a Teams Card to push to the user. We also have a function for that. </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist139246108\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-handle_teams_event6-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;handle_teams_event6.py content, created by KyMidd on 02:57AM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;8\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;handle_teams_event6.py\&quot;>\n        <tr>\n          <td id=\&quot;file-handle_teams_event6-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-handle_teams_event6-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-en>handle_teams_event</span>(<span class=pl-s1>body</span>, <span class=pl-s1>event</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event6-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-handle_teams_event6-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># ...</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event6-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-handle_teams_event6-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>if</span> <span class=pl-s1>event_type</span> <span class=pl-c1>==</span> <span class=pl-s>&amp;#39;message&amp;#39;</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event6-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-handle_teams_event6-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># ...</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event6-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-handle_teams_event6-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># Format the OAuth card</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event6-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-handle_teams_event6-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>card</span> <span class=pl-c1>=</span> <span class=pl-en>format_oauth_card</span>(<span class=pl-s1>auth_url</span>)</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/b713dfe47932e4f860ca9a8fed7d5456/raw/81e3e5d6c75492112e7d92b5b702639db3486601/handle_teams_event6.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/b713dfe47932e4f860ca9a8fed7d5456#file-handle_teams_event6-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          handle_teams_event6.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-110350e56c50.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-110350e56c50.css"><div id="gist139246108" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-handle_teams_event6-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="8" data-paste-markdown-skip="" data-tagsearch-path="handle_teams_event6.py">
        <tbody><tr>
          <td id="file-handle_teams_event6-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-handle_teams_event6-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-en">handle_teams_event</span>(<span class="pl-s1">body</span>, <span class="pl-s1">event</span>):</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event6-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-handle_teams_event6-py-LC2" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># ...</span></td>
        </tr>
        <tr>
          <td id="file-handle_teams_event6-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-handle_teams_event6-py-LC3" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">if</span> <span class="pl-s1">event_type</span> <span class="pl-c1">==</span> <span class="pl-s">'message'</span>:</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event6-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-handle_teams_event6-py-LC4" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># ...</span></td>
        </tr>
        <tr>
          <td id="file-handle_teams_event6-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-handle_teams_event6-py-LC5" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># Format the OAuth card</span></td>
        </tr>
        <tr>
          <td id="file-handle_teams_event6-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-handle_teams_event6-py-LC6" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">card</span> <span class="pl-c1">=</span> <span class="pl-en">format_oauth_card</span>(<span class="pl-s1">auth_url</span>)</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/b713dfe47932e4f860ca9a8fed7d5456/raw/81e3e5d6c75492112e7d92b5b702639db3486601/handle_teams_event6.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/b713dfe47932e4f860ca9a8fed7d5456#file-handle_teams_event6-py" class="Link--inTextBlock">
          handle_teams_event6.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><h2>Build a Clickable SSO Card</h2><p>This function builds a Teams Card that looks like the following:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!nZ7S!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe596be0-16c6-4b37-b13d-d0336461c96a_1108x420.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!nZ7S!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe596be0-16c6-4b37-b13d-d0336461c96a_1108x420.png 424w, https://substackcdn.com/image/fetch/$s_!nZ7S!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe596be0-16c6-4b37-b13d-d0336461c96a_1108x420.png 848w, https://substackcdn.com/image/fetch/$s_!nZ7S!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe596be0-16c6-4b37-b13d-d0336461c96a_1108x420.png 1272w, https://substackcdn.com/image/fetch/$s_!nZ7S!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe596be0-16c6-4b37-b13d-d0336461c96a_1108x420.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!nZ7S!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe596be0-16c6-4b37-b13d-d0336461c96a_1108x420.png" width="1108" height="420" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/be596be0-16c6-4b37-b13d-d0336461c96a_1108x420.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:420,&quot;width&quot;:1108,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:60198,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.letsdodevops.com/i/167005038?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe596be0-16c6-4b37-b13d-d0336461c96a_1108x420.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!nZ7S!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe596be0-16c6-4b37-b13d-d0336461c96a_1108x420.png 424w, https://substackcdn.com/image/fetch/$s_!nZ7S!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe596be0-16c6-4b37-b13d-d0336461c96a_1108x420.png 848w, https://substackcdn.com/image/fetch/$s_!nZ7S!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe596be0-16c6-4b37-b13d-d0336461c96a_1108x420.png 1272w, https://substackcdn.com/image/fetch/$s_!nZ7S!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe596be0-16c6-4b37-b13d-d0336461c96a_1108x420.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The button the users can click takes them to the SSO portal with the URL we just built. </p><p>There&#8217;s not a lot here - we&#8217;re building a few text blocks that contain the weight and layout of our card, as well as a button that opens a URL, which is the Entra SSO sign-in. </p><p>We return it to the parent caller. </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist139246116\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-format_oauth_card-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;format_oauth_card.py content, created by KyMidd on 02:58AM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;8\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;format_oauth_card.py\&quot;>\n        <tr>\n          <td id=\&quot;file-format_oauth_card-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-format_oauth_card-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-en>format_oauth_card</span>(<span class=pl-s1>auth_url</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-format_oauth_card-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-format_oauth_card-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>return</span> {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-format_oauth_card-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-format_oauth_card-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s>&amp;quot;type&amp;quot;</span>: <span class=pl-s>&amp;quot;message&amp;quot;</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-format_oauth_card-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-format_oauth_card-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s>&amp;quot;attachments&amp;quot;</span>: [</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-format_oauth_card-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-format_oauth_card-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-format_oauth_card-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-format_oauth_card-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                <span class=pl-s>&amp;quot;contentType&amp;quot;</span>: <span class=pl-s>&amp;quot;application/vnd.microsoft.card.adaptive&amp;quot;</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-format_oauth_card-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-format_oauth_card-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                <span class=pl-s>&amp;quot;content&amp;quot;</span>: {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-format_oauth_card-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-format_oauth_card-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                    <span class=pl-s>&amp;quot;$schema&amp;quot;</span>: <span class=pl-s>&amp;quot;http://adaptivecards.io/schemas/adaptive-card.json&amp;quot;</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-format_oauth_card-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-format_oauth_card-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                    <span class=pl-s>&amp;quot;type&amp;quot;</span>: <span class=pl-s>&amp;quot;AdaptiveCard&amp;quot;</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-format_oauth_card-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-format_oauth_card-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                    <span class=pl-s>&amp;quot;version&amp;quot;</span>: <span class=pl-s>&amp;quot;1.5&amp;quot;</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-format_oauth_card-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-format_oauth_card-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                    <span class=pl-s>&amp;quot;body&amp;quot;</span>: [</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-format_oauth_card-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-format_oauth_card-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                        {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-format_oauth_card-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-format_oauth_card-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                            <span class=pl-s>&amp;quot;type&amp;quot;</span>: <span class=pl-s>&amp;quot;TextBlock&amp;quot;</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-format_oauth_card-py-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-format_oauth_card-py-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                            <span class=pl-s>&amp;quot;text&amp;quot;</span>: <span class=pl-s>&amp;quot;&#128272; Sign in to authorize this Bot&amp;quot;</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-format_oauth_card-py-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-format_oauth_card-py-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                            <span class=pl-s>&amp;quot;weight&amp;quot;</span>: <span class=pl-s>&amp;quot;Bolder&amp;quot;</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-format_oauth_card-py-L16\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;16\&quot;></td>\n          <td id=\&quot;file-format_oauth_card-py-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                            <span class=pl-s>&amp;quot;size&amp;quot;</span>: <span class=pl-s>&amp;quot;Medium&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-format_oauth_card-py-L17\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;17\&quot;></td>\n          <td id=\&quot;file-format_oauth_card-py-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                        },</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-format_oauth_card-py-L18\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;18\&quot;></td>\n          <td id=\&quot;file-format_oauth_card-py-LC18\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                        {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-format_oauth_card-py-L19\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;19\&quot;></td>\n          <td id=\&quot;file-format_oauth_card-py-LC19\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                            <span class=pl-s>&amp;quot;type&amp;quot;</span>: <span class=pl-s>&amp;quot;TextBlock&amp;quot;</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-format_oauth_card-py-L20\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;20\&quot;></td>\n          <td id=\&quot;file-format_oauth_card-py-LC20\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                            <span class=pl-s>&amp;quot;text&amp;quot;</span>: <span class=pl-s>&amp;quot;To continue, please sign in with your Microsoft account.&amp;quot;</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-format_oauth_card-py-L21\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;21\&quot;></td>\n          <td id=\&quot;file-format_oauth_card-py-LC21\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                            <span class=pl-s>&amp;quot;wrap&amp;quot;</span>: <span class=pl-c1>True</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-format_oauth_card-py-L22\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;22\&quot;></td>\n          <td id=\&quot;file-format_oauth_card-py-LC22\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                        }</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-format_oauth_card-py-L23\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;23\&quot;></td>\n          <td id=\&quot;file-format_oauth_card-py-LC23\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                    ],</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-format_oauth_card-py-L24\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;24\&quot;></td>\n          <td id=\&quot;file-format_oauth_card-py-LC24\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                    <span class=pl-s>&amp;quot;actions&amp;quot;</span>: [</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-format_oauth_card-py-L25\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;25\&quot;></td>\n          <td id=\&quot;file-format_oauth_card-py-LC25\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                        {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-format_oauth_card-py-L26\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;26\&quot;></td>\n          <td id=\&quot;file-format_oauth_card-py-LC26\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                            <span class=pl-s>&amp;quot;type&amp;quot;</span>: <span class=pl-s>&amp;quot;Action.OpenUrl&amp;quot;</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-format_oauth_card-py-L27\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;27\&quot;></td>\n          <td id=\&quot;file-format_oauth_card-py-LC27\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                            <span class=pl-s>&amp;quot;title&amp;quot;</span>: <span class=pl-s>&amp;quot;Sign in with Microsoft&amp;quot;</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-format_oauth_card-py-L28\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;28\&quot;></td>\n          <td id=\&quot;file-format_oauth_card-py-LC28\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                            <span class=pl-s>&amp;quot;url&amp;quot;</span>: <span class=pl-s1>auth_url</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-format_oauth_card-py-L29\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;29\&quot;></td>\n          <td id=\&quot;file-format_oauth_card-py-LC29\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                        }</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-format_oauth_card-py-L30\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;30\&quot;></td>\n          <td id=\&quot;file-format_oauth_card-py-LC30\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                    ]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-format_oauth_card-py-L31\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;31\&quot;></td>\n          <td id=\&quot;file-format_oauth_card-py-LC31\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                }</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-format_oauth_card-py-L32\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;32\&quot;></td>\n          <td id=\&quot;file-format_oauth_card-py-LC32\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            }</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-format_oauth_card-py-L33\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;33\&quot;></td>\n          <td id=\&quot;file-format_oauth_card-py-LC33\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        ]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-format_oauth_card-py-L34\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;34\&quot;></td>\n          <td id=\&quot;file-format_oauth_card-py-LC34\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    }</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/c5ee63d3a47141e0991394c114e2ae40/raw/1b39e14e8582c6255d6444b1ddb2a0a66ae3ca68/format_oauth_card.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/c5ee63d3a47141e0991394c114e2ae40#file-format_oauth_card-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          format_oauth_card.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-110350e56c50.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-110350e56c50.css"><div id="gist139246116" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-format_oauth_card-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="8" data-paste-markdown-skip="" data-tagsearch-path="format_oauth_card.py">
        <tbody><tr>
          <td id="file-format_oauth_card-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-format_oauth_card-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-en">format_oauth_card</span>(<span class="pl-s1">auth_url</span>):</td>
        </tr>
        <tr>
          <td id="file-format_oauth_card-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-format_oauth_card-py-LC2" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">return</span> {</td>
        </tr>
        <tr>
          <td id="file-format_oauth_card-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-format_oauth_card-py-LC3" class="blob-code blob-code-inner js-file-line">        <span class="pl-s">"type"</span>: <span class="pl-s">"message"</span>,</td>
        </tr>
        <tr>
          <td id="file-format_oauth_card-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-format_oauth_card-py-LC4" class="blob-code blob-code-inner js-file-line">        <span class="pl-s">"attachments"</span>: [</td>
        </tr>
        <tr>
          <td id="file-format_oauth_card-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-format_oauth_card-py-LC5" class="blob-code blob-code-inner js-file-line">            {</td>
        </tr>
        <tr>
          <td id="file-format_oauth_card-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-format_oauth_card-py-LC6" class="blob-code blob-code-inner js-file-line">                <span class="pl-s">"contentType"</span>: <span class="pl-s">"application/vnd.microsoft.card.adaptive"</span>,</td>
        </tr>
        <tr>
          <td id="file-format_oauth_card-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-format_oauth_card-py-LC7" class="blob-code blob-code-inner js-file-line">                <span class="pl-s">"content"</span>: {</td>
        </tr>
        <tr>
          <td id="file-format_oauth_card-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-format_oauth_card-py-LC8" class="blob-code blob-code-inner js-file-line">                    <span class="pl-s">"$schema"</span>: <span class="pl-s">"http://adaptivecards.io/schemas/adaptive-card.json"</span>,</td>
        </tr>
        <tr>
          <td id="file-format_oauth_card-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-format_oauth_card-py-LC9" class="blob-code blob-code-inner js-file-line">                    <span class="pl-s">"type"</span>: <span class="pl-s">"AdaptiveCard"</span>,</td>
        </tr>
        <tr>
          <td id="file-format_oauth_card-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-format_oauth_card-py-LC10" class="blob-code blob-code-inner js-file-line">                    <span class="pl-s">"version"</span>: <span class="pl-s">"1.5"</span>,</td>
        </tr>
        <tr>
          <td id="file-format_oauth_card-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-format_oauth_card-py-LC11" class="blob-code blob-code-inner js-file-line">                    <span class="pl-s">"body"</span>: [</td>
        </tr>
        <tr>
          <td id="file-format_oauth_card-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-format_oauth_card-py-LC12" class="blob-code blob-code-inner js-file-line">                        {</td>
        </tr>
        <tr>
          <td id="file-format_oauth_card-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-format_oauth_card-py-LC13" class="blob-code blob-code-inner js-file-line">                            <span class="pl-s">"type"</span>: <span class="pl-s">"TextBlock"</span>,</td>
        </tr>
        <tr>
          <td id="file-format_oauth_card-py-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-format_oauth_card-py-LC14" class="blob-code blob-code-inner js-file-line">                            <span class="pl-s">"text"</span>: <span class="pl-s">"&#128272; Sign in to authorize this Bot"</span>,</td>
        </tr>
        <tr>
          <td id="file-format_oauth_card-py-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-format_oauth_card-py-LC15" class="blob-code blob-code-inner js-file-line">                            <span class="pl-s">"weight"</span>: <span class="pl-s">"Bolder"</span>,</td>
        </tr>
        <tr>
          <td id="file-format_oauth_card-py-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-format_oauth_card-py-LC16" class="blob-code blob-code-inner js-file-line">                            <span class="pl-s">"size"</span>: <span class="pl-s">"Medium"</span></td>
        </tr>
        <tr>
          <td id="file-format_oauth_card-py-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-format_oauth_card-py-LC17" class="blob-code blob-code-inner js-file-line">                        },</td>
        </tr>
        <tr>
          <td id="file-format_oauth_card-py-L18" class="blob-num js-line-number js-blob-rnum" data-line-number="18"></td>
          <td id="file-format_oauth_card-py-LC18" class="blob-code blob-code-inner js-file-line">                        {</td>
        </tr>
        <tr>
          <td id="file-format_oauth_card-py-L19" class="blob-num js-line-number js-blob-rnum" data-line-number="19"></td>
          <td id="file-format_oauth_card-py-LC19" class="blob-code blob-code-inner js-file-line">                            <span class="pl-s">"type"</span>: <span class="pl-s">"TextBlock"</span>,</td>
        </tr>
        <tr>
          <td id="file-format_oauth_card-py-L20" class="blob-num js-line-number js-blob-rnum" data-line-number="20"></td>
          <td id="file-format_oauth_card-py-LC20" class="blob-code blob-code-inner js-file-line">                            <span class="pl-s">"text"</span>: <span class="pl-s">"To continue, please sign in with your Microsoft account."</span>,</td>
        </tr>
        <tr>
          <td id="file-format_oauth_card-py-L21" class="blob-num js-line-number js-blob-rnum" data-line-number="21"></td>
          <td id="file-format_oauth_card-py-LC21" class="blob-code blob-code-inner js-file-line">                            <span class="pl-s">"wrap"</span>: <span class="pl-c1">True</span></td>
        </tr>
        <tr>
          <td id="file-format_oauth_card-py-L22" class="blob-num js-line-number js-blob-rnum" data-line-number="22"></td>
          <td id="file-format_oauth_card-py-LC22" class="blob-code blob-code-inner js-file-line">                        }</td>
        </tr>
        <tr>
          <td id="file-format_oauth_card-py-L23" class="blob-num js-line-number js-blob-rnum" data-line-number="23"></td>
          <td id="file-format_oauth_card-py-LC23" class="blob-code blob-code-inner js-file-line">                    ],</td>
        </tr>
        <tr>
          <td id="file-format_oauth_card-py-L24" class="blob-num js-line-number js-blob-rnum" data-line-number="24"></td>
          <td id="file-format_oauth_card-py-LC24" class="blob-code blob-code-inner js-file-line">                    <span class="pl-s">"actions"</span>: [</td>
        </tr>
        <tr>
          <td id="file-format_oauth_card-py-L25" class="blob-num js-line-number js-blob-rnum" data-line-number="25"></td>
          <td id="file-format_oauth_card-py-LC25" class="blob-code blob-code-inner js-file-line">                        {</td>
        </tr>
        <tr>
          <td id="file-format_oauth_card-py-L26" class="blob-num js-line-number js-blob-rnum" data-line-number="26"></td>
          <td id="file-format_oauth_card-py-LC26" class="blob-code blob-code-inner js-file-line">                            <span class="pl-s">"type"</span>: <span class="pl-s">"Action.OpenUrl"</span>,</td>
        </tr>
        <tr>
          <td id="file-format_oauth_card-py-L27" class="blob-num js-line-number js-blob-rnum" data-line-number="27"></td>
          <td id="file-format_oauth_card-py-LC27" class="blob-code blob-code-inner js-file-line">                            <span class="pl-s">"title"</span>: <span class="pl-s">"Sign in with Microsoft"</span>,</td>
        </tr>
        <tr>
          <td id="file-format_oauth_card-py-L28" class="blob-num js-line-number js-blob-rnum" data-line-number="28"></td>
          <td id="file-format_oauth_card-py-LC28" class="blob-code blob-code-inner js-file-line">                            <span class="pl-s">"url"</span>: <span class="pl-s1">auth_url</span></td>
        </tr>
        <tr>
          <td id="file-format_oauth_card-py-L29" class="blob-num js-line-number js-blob-rnum" data-line-number="29"></td>
          <td id="file-format_oauth_card-py-LC29" class="blob-code blob-code-inner js-file-line">                        }</td>
        </tr>
        <tr>
          <td id="file-format_oauth_card-py-L30" class="blob-num js-line-number js-blob-rnum" data-line-number="30"></td>
          <td id="file-format_oauth_card-py-LC30" class="blob-code blob-code-inner js-file-line">                    ]</td>
        </tr>
        <tr>
          <td id="file-format_oauth_card-py-L31" class="blob-num js-line-number js-blob-rnum" data-line-number="31"></td>
          <td id="file-format_oauth_card-py-LC31" class="blob-code blob-code-inner js-file-line">                }</td>
        </tr>
        <tr>
          <td id="file-format_oauth_card-py-L32" class="blob-num js-line-number js-blob-rnum" data-line-number="32"></td>
          <td id="file-format_oauth_card-py-LC32" class="blob-code blob-code-inner js-file-line">            }</td>
        </tr>
        <tr>
          <td id="file-format_oauth_card-py-L33" class="blob-num js-line-number js-blob-rnum" data-line-number="33"></td>
          <td id="file-format_oauth_card-py-LC33" class="blob-code blob-code-inner js-file-line">        ]</td>
        </tr>
        <tr>
          <td id="file-format_oauth_card-py-L34" class="blob-num js-line-number js-blob-rnum" data-line-number="34"></td>
          <td id="file-format_oauth_card-py-LC34" class="blob-code blob-code-inner js-file-line">    }</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/c5ee63d3a47141e0991394c114e2ae40/raw/1b39e14e8582c6255d6444b1ddb2a0a66ae3ca68/format_oauth_card.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/c5ee63d3a47141e0991394c114e2ae40#file-format_oauth_card-py" class="Link--inTextBlock">
          format_oauth_card.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><h2>Here&#8217;s My Business Card</h2><p>Back to our teams handler. We need to send the SSO Card to the user as a Bot post. </p><p>We identify a few required items, like the response_url (the URL we can POST a response in the same conversation to), from 2 precursor items, both provided in our Teams webhook, line 6-8. </p><p>If we get this far (line 11), we create our headers using the bot bearer token and post the card to the User, line 16. </p><p>We check for status on line 22, and throw an error if it failed. If all worked, we return, line 26. </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist139246148\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-handle_teams_event7-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;handle_teams_event7.py content, created by KyMidd on 03:00AM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;8\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;handle_teams_event7.py\&quot;>\n        <tr>\n          <td id=\&quot;file-handle_teams_event7-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-handle_teams_event7-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-en>handle_teams_event</span>(<span class=pl-s1>body</span>, <span class=pl-s1>event</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event7-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-handle_teams_event7-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># ...</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event7-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-handle_teams_event7-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>if</span> <span class=pl-s1>event_type</span> <span class=pl-c1>==</span> <span class=pl-s>&amp;#39;message&amp;#39;</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event7-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-handle_teams_event7-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># ...</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event7-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-handle_teams_event7-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># Find the service URL and conversation ID from the event body</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event7-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-handle_teams_event7-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>service_url</span> <span class=pl-c1>=</span> <span class=pl-s1>body</span>[<span class=pl-s>&amp;quot;serviceUrl&amp;quot;</span>]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event7-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-handle_teams_event7-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>conversation_id</span> <span class=pl-c1>=</span> <span class=pl-s1>body</span>[<span class=pl-s>&amp;quot;conversation&amp;quot;</span>][<span class=pl-s>&amp;quot;id&amp;quot;</span>]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event7-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-handle_teams_event7-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>response_url</span> <span class=pl-c1>=</span> <span class=pl-s>f&amp;quot;<span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>service_url</span><span class=pl-kos>}</span></span>/v3/conversations/<span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>conversation_id</span><span class=pl-kos>}</span></span>/activities&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event7-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-handle_teams_event7-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event7-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-handle_teams_event7-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># Send the card to the user</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event7-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-handle_teams_event7-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>if</span> <span class=pl-s1>response_url</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event7-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-handle_teams_event7-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>headers</span> <span class=pl-c1>=</span> {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event7-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-handle_teams_event7-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                <span class=pl-s>&amp;quot;Authorization&amp;quot;</span>: <span class=pl-s>f&amp;quot;Bearer <span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>bot_bearer_token</span><span class=pl-kos>}</span></span>&amp;quot;</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event7-py-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-handle_teams_event7-py-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                <span class=pl-s>&amp;quot;Content-Type&amp;quot;</span>: <span class=pl-s>&amp;quot;application/json&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event7-py-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-handle_teams_event7-py-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            }</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event7-py-L16\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;16\&quot;></td>\n          <td id=\&quot;file-handle_teams_event7-py-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>response</span> <span class=pl-c1>=</span> <span class=pl-s1>requests</span>.<span class=pl-c1>post</span>(<span class=pl-s1>response_url</span>, <span class=pl-s1>headers</span><span class=pl-c1>=</span><span class=pl-s1>headers</span>, <span class=pl-s1>json</span><span class=pl-c1>=</span><span class=pl-s1>card</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event7-py-L17\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;17\&quot;></td>\n          <td id=\&quot;file-handle_teams_event7-py-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event7-py-L18\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;18\&quot;></td>\n          <td id=\&quot;file-handle_teams_event7-py-LC18\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-c># Read VERA_DEBUG from environment variable</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event7-py-L19\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;19\&quot;></td>\n          <td id=\&quot;file-handle_teams_event7-py-LC19\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-k>if</span> <span class=pl-s1>os</span>.<span class=pl-c1>environ</span>.<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;VERA_DEBUG&amp;quot;</span>) <span class=pl-c1>==</span> <span class=pl-s>&amp;quot;True&amp;quot;</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event7-py-L20\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;20\&quot;></td>\n          <td id=\&quot;file-handle_teams_event7-py-LC20\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                <span class=pl-en>print</span>(<span class=pl-s>&amp;quot;&#128994; Response from sending auth card:&amp;quot;</span>, <span class=pl-s1>response</span>.<span class=pl-c1>text</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event7-py-L21\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;21\&quot;></td>\n          <td id=\&quot;file-handle_teams_event7-py-LC21\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event7-py-L22\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;22\&quot;></td>\n          <td id=\&quot;file-handle_teams_event7-py-LC22\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s1>response</span>.<span class=pl-c1>raise_for_status</span>()</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event7-py-L23\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;23\&quot;></td>\n          <td id=\&quot;file-handle_teams_event7-py-LC23\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-en>print</span>(<span class=pl-s>&amp;quot;&#128994; Auth card sent successfully&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event7-py-L24\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;24\&quot;></td>\n          <td id=\&quot;file-handle_teams_event7-py-LC24\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event7-py-L25\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;25\&quot;></td>\n          <td id=\&quot;file-handle_teams_event7-py-LC25\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-c># All done, return</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_teams_event7-py-L26\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;26\&quot;></td>\n          <td id=\&quot;file-handle_teams_event7-py-LC26\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-k>return</span></td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/f3e0570fa13a64ae52162cc5b42f4e76/raw/e1bc6b87622e634225afc9ba8dbd9ffc81d04560/handle_teams_event7.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/f3e0570fa13a64ae52162cc5b42f4e76#file-handle_teams_event7-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          handle_teams_event7.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-110350e56c50.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-110350e56c50.css"><div id="gist139246148" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-handle_teams_event7-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="8" data-paste-markdown-skip="" data-tagsearch-path="handle_teams_event7.py">
        <tbody><tr>
          <td id="file-handle_teams_event7-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-handle_teams_event7-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-en">handle_teams_event</span>(<span class="pl-s1">body</span>, <span class="pl-s1">event</span>):</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event7-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-handle_teams_event7-py-LC2" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># ...</span></td>
        </tr>
        <tr>
          <td id="file-handle_teams_event7-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-handle_teams_event7-py-LC3" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">if</span> <span class="pl-s1">event_type</span> <span class="pl-c1">==</span> <span class="pl-s">'message'</span>:</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event7-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-handle_teams_event7-py-LC4" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># ...</span></td>
        </tr>
        <tr>
          <td id="file-handle_teams_event7-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-handle_teams_event7-py-LC5" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># Find the service URL and conversation ID from the event body</span></td>
        </tr>
        <tr>
          <td id="file-handle_teams_event7-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-handle_teams_event7-py-LC6" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">service_url</span> <span class="pl-c1">=</span> <span class="pl-s1">body</span>[<span class="pl-s">"serviceUrl"</span>]</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event7-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-handle_teams_event7-py-LC7" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">conversation_id</span> <span class="pl-c1">=</span> <span class="pl-s1">body</span>[<span class="pl-s">"conversation"</span>][<span class="pl-s">"id"</span>]</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event7-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-handle_teams_event7-py-LC8" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">response_url</span> <span class="pl-c1">=</span> <span class="pl-s">f"<span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">service_url</span><span class="pl-kos">}</span></span>/v3/conversations/<span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">conversation_id</span><span class="pl-kos">}</span></span>/activities"</span></td>
        </tr>
        <tr>
          <td id="file-handle_teams_event7-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-handle_teams_event7-py-LC9" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event7-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-handle_teams_event7-py-LC10" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># Send the card to the user</span></td>
        </tr>
        <tr>
          <td id="file-handle_teams_event7-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-handle_teams_event7-py-LC11" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">if</span> <span class="pl-s1">response_url</span>:</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event7-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-handle_teams_event7-py-LC12" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">headers</span> <span class="pl-c1">=</span> {</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event7-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-handle_teams_event7-py-LC13" class="blob-code blob-code-inner js-file-line">                <span class="pl-s">"Authorization"</span>: <span class="pl-s">f"Bearer <span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">bot_bearer_token</span><span class="pl-kos">}</span></span>"</span>,</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event7-py-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-handle_teams_event7-py-LC14" class="blob-code blob-code-inner js-file-line">                <span class="pl-s">"Content-Type"</span>: <span class="pl-s">"application/json"</span></td>
        </tr>
        <tr>
          <td id="file-handle_teams_event7-py-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-handle_teams_event7-py-LC15" class="blob-code blob-code-inner js-file-line">            }</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event7-py-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-handle_teams_event7-py-LC16" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">response</span> <span class="pl-c1">=</span> <span class="pl-s1">requests</span>.<span class="pl-c1">post</span>(<span class="pl-s1">response_url</span>, <span class="pl-s1">headers</span><span class="pl-c1">=</span><span class="pl-s1">headers</span>, <span class="pl-s1">json</span><span class="pl-c1">=</span><span class="pl-s1">card</span>)</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event7-py-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-handle_teams_event7-py-LC17" class="blob-code blob-code-inner js-file-line">            </td>
        </tr>
        <tr>
          <td id="file-handle_teams_event7-py-L18" class="blob-num js-line-number js-blob-rnum" data-line-number="18"></td>
          <td id="file-handle_teams_event7-py-LC18" class="blob-code blob-code-inner js-file-line">            <span class="pl-c"># Read VERA_DEBUG from environment variable</span></td>
        </tr>
        <tr>
          <td id="file-handle_teams_event7-py-L19" class="blob-num js-line-number js-blob-rnum" data-line-number="19"></td>
          <td id="file-handle_teams_event7-py-LC19" class="blob-code blob-code-inner js-file-line">            <span class="pl-k">if</span> <span class="pl-s1">os</span>.<span class="pl-c1">environ</span>.<span class="pl-c1">get</span>(<span class="pl-s">"VERA_DEBUG"</span>) <span class="pl-c1">==</span> <span class="pl-s">"True"</span>:</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event7-py-L20" class="blob-num js-line-number js-blob-rnum" data-line-number="20"></td>
          <td id="file-handle_teams_event7-py-LC20" class="blob-code blob-code-inner js-file-line">                <span class="pl-en">print</span>(<span class="pl-s">"&#128994; Response from sending auth card:"</span>, <span class="pl-s1">response</span>.<span class="pl-c1">text</span>)</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event7-py-L21" class="blob-num js-line-number js-blob-rnum" data-line-number="21"></td>
          <td id="file-handle_teams_event7-py-LC21" class="blob-code blob-code-inner js-file-line">            </td>
        </tr>
        <tr>
          <td id="file-handle_teams_event7-py-L22" class="blob-num js-line-number js-blob-rnum" data-line-number="22"></td>
          <td id="file-handle_teams_event7-py-LC22" class="blob-code blob-code-inner js-file-line">            <span class="pl-s1">response</span>.<span class="pl-c1">raise_for_status</span>()</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event7-py-L23" class="blob-num js-line-number js-blob-rnum" data-line-number="23"></td>
          <td id="file-handle_teams_event7-py-LC23" class="blob-code blob-code-inner js-file-line">            <span class="pl-en">print</span>(<span class="pl-s">"&#128994; Auth card sent successfully"</span>)</td>
        </tr>
        <tr>
          <td id="file-handle_teams_event7-py-L24" class="blob-num js-line-number js-blob-rnum" data-line-number="24"></td>
          <td id="file-handle_teams_event7-py-LC24" class="blob-code blob-code-inner js-file-line">            </td>
        </tr>
        <tr>
          <td id="file-handle_teams_event7-py-L25" class="blob-num js-line-number js-blob-rnum" data-line-number="25"></td>
          <td id="file-handle_teams_event7-py-LC25" class="blob-code blob-code-inner js-file-line">            <span class="pl-c"># All done, return</span></td>
        </tr>
        <tr>
          <td id="file-handle_teams_event7-py-L26" class="blob-num js-line-number js-blob-rnum" data-line-number="26"></td>
          <td id="file-handle_teams_event7-py-LC26" class="blob-code blob-code-inner js-file-line">            <span class="pl-k">return</span></td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/f3e0570fa13a64ae52162cc5b42f4e76/raw/e1bc6b87622e634225afc9ba8dbd9ffc81d04560/handle_teams_event7.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/f3e0570fa13a64ae52162cc5b42f4e76#file-handle_teams_event7-py" class="Link--inTextBlock">
          handle_teams_event7.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>That concludes the &#8220;Teams&#8221; workflow. </p><p>However! We haven&#8217;t handled the OAuth2 routing yet. If we receive a token from Entra, what do we do with it? Let&#8217;s cover that next. </p><h1>Auth Code POST from Entra IdP</h1><p>Now, what if Entra sends us a token? Well, it doesn&#8217;t. It does however, send us an &#8220;auth_token&#8221; that we can exchange for a token (big sigh, $MSFT). Lets get to it. </p><p>When the user approves the SSO login, Microsoft Entra IdP will send an auth_token payload to us on the /callback URI, so we check for that on line 4. </p><p>We look up the auth code, line 8 (yay!), as well as the aad_object_id (user&#8217;s Entra ID), line 9. </p><p>Then we build an auto-close page. We do this because the User - like, the real human user, gets redirected to our lambda function URL once they approve the SSO IdP issuance. That&#8217;s kinda weird, but we can handle it by giving them a javascript payload that&#8217;ll redirect them back to Teams and attempt to close their tab. </p><p>And zooooooming in. </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist139246378\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-lambda_handler_3-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;lambda_handler_3.py content, created by KyMidd on 03:18AM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;8\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;lambda_handler_3.py\&quot;>\n        <tr>\n          <td id=\&quot;file-lambda_handler_3-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-lambda_handler_3-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-en>lambda_handler</span>(<span class=pl-s1>event</span>, <span class=pl-s1>context</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler_3-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-lambda_handler_3-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># ...    </span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler_3-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-lambda_handler_3-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Check if the event POSTED to URI /callback</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler_3-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-lambda_handler_3-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>if</span> <span class=pl-s1>event</span>.<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;rawPath&amp;quot;</span>) <span class=pl-c1>==</span> <span class=pl-s>&amp;quot;/callback&amp;quot;</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler_3-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-lambda_handler_3-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-en>print</span>(<span class=pl-s>&amp;quot;&#128994; Received callback event&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler_3-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-lambda_handler_3-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>             </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler_3-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-lambda_handler_3-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># Set values to vars</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler_3-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-lambda_handler_3-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>auth_code</span> <span class=pl-c1>=</span> <span class=pl-s1>event</span>[<span class=pl-s>&amp;quot;queryStringParameters&amp;quot;</span>].<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;code&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler_3-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-lambda_handler_3-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>aad_object_id</span> <span class=pl-c1>=</span> <span class=pl-s1>event</span>[<span class=pl-s>&amp;quot;queryStringParameters&amp;quot;</span>].<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;state&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler_3-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-lambda_handler_3-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler_3-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-lambda_handler_3-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># Debug</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler_3-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-lambda_handler_3-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>if</span> <span class=pl-s1>os</span>.<span class=pl-c1>environ</span>.<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;VERA_DEBUG&amp;quot;</span>) <span class=pl-c1>==</span> <span class=pl-s>&amp;quot;True&amp;quot;</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler_3-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-lambda_handler_3-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-en>print</span>(<span class=pl-s>&amp;quot;&#128994; Auth Code:&amp;quot;</span>, <span class=pl-s1>auth_code</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler_3-py-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-lambda_handler_3-py-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-en>print</span>(<span class=pl-s>&amp;quot;&#128994; State (which is aad_object_id):&amp;quot;</span>, <span class=pl-s1>aad_object_id</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler_3-py-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-lambda_handler_3-py-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler_3-py-L16\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;16\&quot;></td>\n          <td id=\&quot;file-lambda_handler_3-py-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># Build auto-close page for user</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler_3-py-L17\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;17\&quot;></td>\n          <td id=\&quot;file-lambda_handler_3-py-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s1>autoclose_page</span> <span class=pl-c1>=</span> <span class=pl-en>build_autoclose_page</span>()</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/4dff42b23b83e5ca6c4b42a201a03132/raw/ab9e69e1ba8dfb55512fd5e5c789a684baa2a216/lambda_handler_3.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/4dff42b23b83e5ca6c4b42a201a03132#file-lambda_handler_3-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          lambda_handler_3.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-110350e56c50.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-110350e56c50.css"><div id="gist139246378" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-lambda_handler_3-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="8" data-paste-markdown-skip="" data-tagsearch-path="lambda_handler_3.py">
        <tbody><tr>
          <td id="file-lambda_handler_3-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-lambda_handler_3-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-en">lambda_handler</span>(<span class="pl-s1">event</span>, <span class="pl-s1">context</span>):</td>
        </tr>
        <tr>
          <td id="file-lambda_handler_3-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-lambda_handler_3-py-LC2" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># ...    </span></td>
        </tr>
        <tr>
          <td id="file-lambda_handler_3-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-lambda_handler_3-py-LC3" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Check if the event POSTED to URI /callback</span></td>
        </tr>
        <tr>
          <td id="file-lambda_handler_3-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-lambda_handler_3-py-LC4" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">if</span> <span class="pl-s1">event</span>.<span class="pl-c1">get</span>(<span class="pl-s">"rawPath"</span>) <span class="pl-c1">==</span> <span class="pl-s">"/callback"</span>:</td>
        </tr>
        <tr>
          <td id="file-lambda_handler_3-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-lambda_handler_3-py-LC5" class="blob-code blob-code-inner js-file-line">        <span class="pl-en">print</span>(<span class="pl-s">"&#128994; Received callback event"</span>)</td>
        </tr>
        <tr>
          <td id="file-lambda_handler_3-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-lambda_handler_3-py-LC6" class="blob-code blob-code-inner js-file-line">             </td>
        </tr>
        <tr>
          <td id="file-lambda_handler_3-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-lambda_handler_3-py-LC7" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># Set values to vars</span></td>
        </tr>
        <tr>
          <td id="file-lambda_handler_3-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-lambda_handler_3-py-LC8" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">auth_code</span> <span class="pl-c1">=</span> <span class="pl-s1">event</span>[<span class="pl-s">"queryStringParameters"</span>].<span class="pl-c1">get</span>(<span class="pl-s">"code"</span>)</td>
        </tr>
        <tr>
          <td id="file-lambda_handler_3-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-lambda_handler_3-py-LC9" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">aad_object_id</span> <span class="pl-c1">=</span> <span class="pl-s1">event</span>[<span class="pl-s">"queryStringParameters"</span>].<span class="pl-c1">get</span>(<span class="pl-s">"state"</span>)</td>
        </tr>
        <tr>
          <td id="file-lambda_handler_3-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-lambda_handler_3-py-LC10" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-lambda_handler_3-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-lambda_handler_3-py-LC11" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># Debug</span></td>
        </tr>
        <tr>
          <td id="file-lambda_handler_3-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-lambda_handler_3-py-LC12" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">if</span> <span class="pl-s1">os</span>.<span class="pl-c1">environ</span>.<span class="pl-c1">get</span>(<span class="pl-s">"VERA_DEBUG"</span>) <span class="pl-c1">==</span> <span class="pl-s">"True"</span>:</td>
        </tr>
        <tr>
          <td id="file-lambda_handler_3-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-lambda_handler_3-py-LC13" class="blob-code blob-code-inner js-file-line">            <span class="pl-en">print</span>(<span class="pl-s">"&#128994; Auth Code:"</span>, <span class="pl-s1">auth_code</span>)</td>
        </tr>
        <tr>
          <td id="file-lambda_handler_3-py-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-lambda_handler_3-py-LC14" class="blob-code blob-code-inner js-file-line">            <span class="pl-en">print</span>(<span class="pl-s">"&#128994; State (which is aad_object_id):"</span>, <span class="pl-s1">aad_object_id</span>)</td>
        </tr>
        <tr>
          <td id="file-lambda_handler_3-py-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-lambda_handler_3-py-LC15" class="blob-code blob-code-inner js-file-line">        </td>
        </tr>
        <tr>
          <td id="file-lambda_handler_3-py-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-lambda_handler_3-py-LC16" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># Build auto-close page for user</span></td>
        </tr>
        <tr>
          <td id="file-lambda_handler_3-py-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-lambda_handler_3-py-LC17" class="blob-code blob-code-inner js-file-line">        <span class="pl-s1">autoclose_page</span> <span class="pl-c1">=</span> <span class="pl-en">build_autoclose_page</span>()</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/4dff42b23b83e5ca6c4b42a201a03132/raw/ab9e69e1ba8dfb55512fd5e5c789a684baa2a216/lambda_handler_3.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/4dff42b23b83e5ca6c4b42a201a03132#file-lambda_handler_3-py" class="Link--inTextBlock">
          lambda_handler_3.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><h2>SSO, Then Magically Back to Teams</h2><p>This function builds an html payload with a script that redirects users to Teams, using the msteams handler. In practice, this means that after users approve the SSO prompt in their browser, their computer automatically switches back to the Teams app, which is conveniently now working on a response to their request. </p><p>It&#8217;s a really slick system. </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist139246448\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-build_autoclose_page-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;build_autoclose_page.py content, created by KyMidd on 03:23AM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;8\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;build_autoclose_page.py\&quot;>\n        <tr>\n          <td id=\&quot;file-build_autoclose_page-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-build_autoclose_page-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-en>build_autoclose_page</span>():</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_autoclose_page-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-build_autoclose_page-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>html</span> <span class=pl-c1>=</span> <span class=pl-s>&amp;quot;&amp;quot;&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_autoclose_page-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-build_autoclose_page-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s>    &amp;lt;!DOCTYPE html&amp;gt;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_autoclose_page-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-build_autoclose_page-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s>    &amp;lt;html&amp;gt;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_autoclose_page-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-build_autoclose_page-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s>      &amp;lt;head&amp;gt;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_autoclose_page-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-build_autoclose_page-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s>      &amp;lt;title&amp;gt;Authentication Complete&amp;lt;/title&amp;gt;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_autoclose_page-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-build_autoclose_page-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s>      &amp;lt;script&amp;gt;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_autoclose_page-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-build_autoclose_page-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s>        window.onload = function() {</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_autoclose_page-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-build_autoclose_page-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s>        if (window.opener) {</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_autoclose_page-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-build_autoclose_page-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s>          window.opener.postMessage(&amp;quot;authComplete&amp;quot;, &amp;quot;*&amp;quot;);</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_autoclose_page-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-build_autoclose_page-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s>        }</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_autoclose_page-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-build_autoclose_page-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s>        window.close();</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_autoclose_page-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-build_autoclose_page-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s>        setTimeout(function() {</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_autoclose_page-py-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-build_autoclose_page-py-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s>          window.location.href = &amp;quot;msteams://teams.microsoft.com&amp;quot;;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_autoclose_page-py-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-build_autoclose_page-py-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s>        }, 1000);</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_autoclose_page-py-L16\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;16\&quot;></td>\n          <td id=\&quot;file-build_autoclose_page-py-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s>        };</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_autoclose_page-py-L17\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;17\&quot;></td>\n          <td id=\&quot;file-build_autoclose_page-py-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s>      &amp;lt;/script&amp;gt;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_autoclose_page-py-L18\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;18\&quot;></td>\n          <td id=\&quot;file-build_autoclose_page-py-LC18\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s>      &amp;lt;/head&amp;gt;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_autoclose_page-py-L19\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;19\&quot;></td>\n          <td id=\&quot;file-build_autoclose_page-py-LC19\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s>      &amp;lt;body&amp;gt;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_autoclose_page-py-L20\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;20\&quot;></td>\n          <td id=\&quot;file-build_autoclose_page-py-LC20\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s>      &amp;lt;p&amp;gt;Successfully logged in, you can close this window.&amp;lt;/p&amp;gt;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_autoclose_page-py-L21\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;21\&quot;></td>\n          <td id=\&quot;file-build_autoclose_page-py-LC21\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s>      &amp;lt;/body&amp;gt;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_autoclose_page-py-L22\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;22\&quot;></td>\n          <td id=\&quot;file-build_autoclose_page-py-LC22\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s>    &amp;lt;/html&amp;gt;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_autoclose_page-py-L23\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;23\&quot;></td>\n          <td id=\&quot;file-build_autoclose_page-py-LC23\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-s>    &amp;quot;&amp;quot;&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_autoclose_page-py-L24\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;24\&quot;></td>\n          <td id=\&quot;file-build_autoclose_page-py-LC24\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_autoclose_page-py-L25\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;25\&quot;></td>\n          <td id=\&quot;file-build_autoclose_page-py-LC25\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Return the html </span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-build_autoclose_page-py-L26\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;26\&quot;></td>\n          <td id=\&quot;file-build_autoclose_page-py-LC26\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>return</span> <span class=pl-s1>html</span></td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/1d7c6ba79141975018c5547e06ff7134/raw/9609cb7c0244ac456889b8b028b5e3415b006441/build_autoclose_page.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/1d7c6ba79141975018c5547e06ff7134#file-build_autoclose_page-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          build_autoclose_page.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-110350e56c50.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-110350e56c50.css"><div id="gist139246448" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-build_autoclose_page-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="8" data-paste-markdown-skip="" data-tagsearch-path="build_autoclose_page.py">
        <tbody><tr>
          <td id="file-build_autoclose_page-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-build_autoclose_page-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-en">build_autoclose_page</span>():</td>
        </tr>
        <tr>
          <td id="file-build_autoclose_page-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-build_autoclose_page-py-LC2" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">html</span> <span class="pl-c1">=</span> <span class="pl-s">"""</span></td>
        </tr>
        <tr>
          <td id="file-build_autoclose_page-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-build_autoclose_page-py-LC3" class="blob-code blob-code-inner js-file-line"><span class="pl-s">    &lt;!DOCTYPE html&gt;</span></td>
        </tr>
        <tr>
          <td id="file-build_autoclose_page-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-build_autoclose_page-py-LC4" class="blob-code blob-code-inner js-file-line"><span class="pl-s">    &lt;html&gt;</span></td>
        </tr>
        <tr>
          <td id="file-build_autoclose_page-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-build_autoclose_page-py-LC5" class="blob-code blob-code-inner js-file-line"><span class="pl-s">      &lt;head&gt;</span></td>
        </tr>
        <tr>
          <td id="file-build_autoclose_page-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-build_autoclose_page-py-LC6" class="blob-code blob-code-inner js-file-line"><span class="pl-s">      &lt;title&gt;Authentication Complete&lt;/title&gt;</span></td>
        </tr>
        <tr>
          <td id="file-build_autoclose_page-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-build_autoclose_page-py-LC7" class="blob-code blob-code-inner js-file-line"><span class="pl-s">      &lt;script&gt;</span></td>
        </tr>
        <tr>
          <td id="file-build_autoclose_page-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-build_autoclose_page-py-LC8" class="blob-code blob-code-inner js-file-line"><span class="pl-s">        window.onload = function() {</span></td>
        </tr>
        <tr>
          <td id="file-build_autoclose_page-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-build_autoclose_page-py-LC9" class="blob-code blob-code-inner js-file-line"><span class="pl-s">        if (window.opener) {</span></td>
        </tr>
        <tr>
          <td id="file-build_autoclose_page-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-build_autoclose_page-py-LC10" class="blob-code blob-code-inner js-file-line"><span class="pl-s">          window.opener.postMessage("authComplete", "*");</span></td>
        </tr>
        <tr>
          <td id="file-build_autoclose_page-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-build_autoclose_page-py-LC11" class="blob-code blob-code-inner js-file-line"><span class="pl-s">        }</span></td>
        </tr>
        <tr>
          <td id="file-build_autoclose_page-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-build_autoclose_page-py-LC12" class="blob-code blob-code-inner js-file-line"><span class="pl-s">        window.close();</span></td>
        </tr>
        <tr>
          <td id="file-build_autoclose_page-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-build_autoclose_page-py-LC13" class="blob-code blob-code-inner js-file-line"><span class="pl-s">        setTimeout(function() {</span></td>
        </tr>
        <tr>
          <td id="file-build_autoclose_page-py-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-build_autoclose_page-py-LC14" class="blob-code blob-code-inner js-file-line"><span class="pl-s">          window.location.href = "msteams://teams.microsoft.com";</span></td>
        </tr>
        <tr>
          <td id="file-build_autoclose_page-py-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-build_autoclose_page-py-LC15" class="blob-code blob-code-inner js-file-line"><span class="pl-s">        }, 1000);</span></td>
        </tr>
        <tr>
          <td id="file-build_autoclose_page-py-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-build_autoclose_page-py-LC16" class="blob-code blob-code-inner js-file-line"><span class="pl-s">        };</span></td>
        </tr>
        <tr>
          <td id="file-build_autoclose_page-py-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-build_autoclose_page-py-LC17" class="blob-code blob-code-inner js-file-line"><span class="pl-s">      &lt;/script&gt;</span></td>
        </tr>
        <tr>
          <td id="file-build_autoclose_page-py-L18" class="blob-num js-line-number js-blob-rnum" data-line-number="18"></td>
          <td id="file-build_autoclose_page-py-LC18" class="blob-code blob-code-inner js-file-line"><span class="pl-s">      &lt;/head&gt;</span></td>
        </tr>
        <tr>
          <td id="file-build_autoclose_page-py-L19" class="blob-num js-line-number js-blob-rnum" data-line-number="19"></td>
          <td id="file-build_autoclose_page-py-LC19" class="blob-code blob-code-inner js-file-line"><span class="pl-s">      &lt;body&gt;</span></td>
        </tr>
        <tr>
          <td id="file-build_autoclose_page-py-L20" class="blob-num js-line-number js-blob-rnum" data-line-number="20"></td>
          <td id="file-build_autoclose_page-py-LC20" class="blob-code blob-code-inner js-file-line"><span class="pl-s">      &lt;p&gt;Successfully logged in, you can close this window.&lt;/p&gt;</span></td>
        </tr>
        <tr>
          <td id="file-build_autoclose_page-py-L21" class="blob-num js-line-number js-blob-rnum" data-line-number="21"></td>
          <td id="file-build_autoclose_page-py-LC21" class="blob-code blob-code-inner js-file-line"><span class="pl-s">      &lt;/body&gt;</span></td>
        </tr>
        <tr>
          <td id="file-build_autoclose_page-py-L22" class="blob-num js-line-number js-blob-rnum" data-line-number="22"></td>
          <td id="file-build_autoclose_page-py-LC22" class="blob-code blob-code-inner js-file-line"><span class="pl-s">    &lt;/html&gt;</span></td>
        </tr>
        <tr>
          <td id="file-build_autoclose_page-py-L23" class="blob-num js-line-number js-blob-rnum" data-line-number="23"></td>
          <td id="file-build_autoclose_page-py-LC23" class="blob-code blob-code-inner js-file-line"><span class="pl-s">    """</span></td>
        </tr>
        <tr>
          <td id="file-build_autoclose_page-py-L24" class="blob-num js-line-number js-blob-rnum" data-line-number="24"></td>
          <td id="file-build_autoclose_page-py-LC24" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-build_autoclose_page-py-L25" class="blob-num js-line-number js-blob-rnum" data-line-number="25"></td>
          <td id="file-build_autoclose_page-py-LC25" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Return the html </span></td>
        </tr>
        <tr>
          <td id="file-build_autoclose_page-py-L26" class="blob-num js-line-number js-blob-rnum" data-line-number="26"></td>
          <td id="file-build_autoclose_page-py-LC26" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">return</span> <span class="pl-s1">html</span></td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/1d7c6ba79141975018c5547e06ff7134/raw/9609cb7c0244ac456889b8b028b5e3415b006441/build_autoclose_page.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/1d7c6ba79141975018c5547e06ff7134#file-build_autoclose_page-py" class="Link--inTextBlock">
          build_autoclose_page.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>Once we receive the html payload, we do all the auth code magic. We&#8217;ll dig into that shortly, but this function is almost complete, so lets look a bit ahead. </p><p>On line 9, we send back an http/200 and close out the page for the user. </p><p>I initially had this return happen before I worked the callback, but when lambdas send a response, they end, so I need to do the quick work of handling the auth code exchange and kicking on the Worker before we do that. In practice, that means users hang on the SSO approval screen for a second or two before the redirect loads. They barely notice. </p><p>Lets zoom in on how we handle the auth code callback. </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist139246499\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-lambda_handler_4-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;lambda_handler_4.py content, created by KyMidd on 03:26AM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;8\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;lambda_handler_4.py\&quot;>\n        <tr>\n          <td id=\&quot;file-lambda_handler_4-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-lambda_handler_4-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-en>lambda_handler</span>(<span class=pl-s1>event</span>, <span class=pl-s1>context</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler_4-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-lambda_handler_4-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># ...    </span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler_4-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-lambda_handler_4-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Check if the event POSTED to URI /callback</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler_4-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-lambda_handler_4-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>if</span> <span class=pl-s1>event</span>.<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;rawPath&amp;quot;</span>) <span class=pl-c1>==</span> <span class=pl-s>&amp;quot;/callback&amp;quot;</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler_4-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-lambda_handler_4-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># Handle the &amp;quot;auth code&amp;quot; callback event when we receive the auth code from $MSFT</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler_4-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-lambda_handler_4-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-en>handle_auth_code_callback</span>(<span class=pl-s1>body</span>, <span class=pl-s1>event</span>, <span class=pl-s1>auth_code</span>, <span class=pl-s1>aad_object_id</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler_4-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-lambda_handler_4-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler_4-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-lambda_handler_4-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-c># Return the HTML page to the user</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler_4-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-lambda_handler_4-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-k>return</span> {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler_4-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-lambda_handler_4-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s>&amp;#39;statusCode&amp;#39;</span>: <span class=pl-c1>200</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler_4-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-lambda_handler_4-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s>&amp;#39;headers&amp;#39;</span>: {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler_4-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-lambda_handler_4-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>                <span class=pl-s>&amp;#39;Content-Type&amp;#39;</span>: <span class=pl-s>&amp;#39;text/html&amp;#39;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler_4-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-lambda_handler_4-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            },</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler_4-py-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-lambda_handler_4-py-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s>&amp;#39;body&amp;#39;</span>: <span class=pl-s1>autoclose_page</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-lambda_handler_4-py-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-lambda_handler_4-py-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        }</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/94405cfece031956dfe09051639e0a22/raw/020ee2ce6029149d96cbcba19497af5afc7df783/lambda_handler_4.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/94405cfece031956dfe09051639e0a22#file-lambda_handler_4-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          lambda_handler_4.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-110350e56c50.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-110350e56c50.css"><div id="gist139246499" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-lambda_handler_4-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="8" data-paste-markdown-skip="" data-tagsearch-path="lambda_handler_4.py">
        <tbody><tr>
          <td id="file-lambda_handler_4-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-lambda_handler_4-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-en">lambda_handler</span>(<span class="pl-s1">event</span>, <span class="pl-s1">context</span>):</td>
        </tr>
        <tr>
          <td id="file-lambda_handler_4-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-lambda_handler_4-py-LC2" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># ...    </span></td>
        </tr>
        <tr>
          <td id="file-lambda_handler_4-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-lambda_handler_4-py-LC3" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Check if the event POSTED to URI /callback</span></td>
        </tr>
        <tr>
          <td id="file-lambda_handler_4-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-lambda_handler_4-py-LC4" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">if</span> <span class="pl-s1">event</span>.<span class="pl-c1">get</span>(<span class="pl-s">"rawPath"</span>) <span class="pl-c1">==</span> <span class="pl-s">"/callback"</span>:</td>
        </tr>
        <tr>
          <td id="file-lambda_handler_4-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-lambda_handler_4-py-LC5" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># Handle the "auth code" callback event when we receive the auth code from $MSFT</span></td>
        </tr>
        <tr>
          <td id="file-lambda_handler_4-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-lambda_handler_4-py-LC6" class="blob-code blob-code-inner js-file-line">        <span class="pl-en">handle_auth_code_callback</span>(<span class="pl-s1">body</span>, <span class="pl-s1">event</span>, <span class="pl-s1">auth_code</span>, <span class="pl-s1">aad_object_id</span>)</td>
        </tr>
        <tr>
          <td id="file-lambda_handler_4-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-lambda_handler_4-py-LC7" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-lambda_handler_4-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-lambda_handler_4-py-LC8" class="blob-code blob-code-inner js-file-line">        <span class="pl-c"># Return the HTML page to the user</span></td>
        </tr>
        <tr>
          <td id="file-lambda_handler_4-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-lambda_handler_4-py-LC9" class="blob-code blob-code-inner js-file-line">        <span class="pl-k">return</span> {</td>
        </tr>
        <tr>
          <td id="file-lambda_handler_4-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-lambda_handler_4-py-LC10" class="blob-code blob-code-inner js-file-line">            <span class="pl-s">'statusCode'</span>: <span class="pl-c1">200</span>,</td>
        </tr>
        <tr>
          <td id="file-lambda_handler_4-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-lambda_handler_4-py-LC11" class="blob-code blob-code-inner js-file-line">            <span class="pl-s">'headers'</span>: {</td>
        </tr>
        <tr>
          <td id="file-lambda_handler_4-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-lambda_handler_4-py-LC12" class="blob-code blob-code-inner js-file-line">                <span class="pl-s">'Content-Type'</span>: <span class="pl-s">'text/html'</span></td>
        </tr>
        <tr>
          <td id="file-lambda_handler_4-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-lambda_handler_4-py-LC13" class="blob-code blob-code-inner js-file-line">            },</td>
        </tr>
        <tr>
          <td id="file-lambda_handler_4-py-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-lambda_handler_4-py-LC14" class="blob-code blob-code-inner js-file-line">            <span class="pl-s">'body'</span>: <span class="pl-s1">autoclose_page</span></td>
        </tr>
        <tr>
          <td id="file-lambda_handler_4-py-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-lambda_handler_4-py-LC15" class="blob-code blob-code-inner js-file-line">        }</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/94405cfece031956dfe09051639e0a22/raw/020ee2ce6029149d96cbcba19497af5afc7df783/lambda_handler_4.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/94405cfece031956dfe09051639e0a22#file-lambda_handler_4-py" class="Link--inTextBlock">
          lambda_handler_4.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>Lets walk through how we handle an auth token from Entra. </p><p>We look up our table ARNs, line 4-5, and fetch our secret package, line 9, then disambiguate them lines 12-15. I won&#8217;t dig into this, because we did earlier. </p><p>On line 21, we do the token exchange - an auth code for an auth token that&#8217;s valid for Graph API. </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist139246533\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-handle_auth_code_callback1-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;handle_auth_code_callback1.py content, created by KyMidd on 03:29AM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;8\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;handle_auth_code_callback1.py\&quot;>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback1-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback1-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-en>handle_auth_code_callback</span>(<span class=pl-s1>body</span>, <span class=pl-s1>event</span>, <span class=pl-s1>auth_code</span>, <span class=pl-s1>aad_object_id</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback1-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback1-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback1-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback1-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Read table names from environment variables</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback1-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback1-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>conversation_table_arn</span> <span class=pl-c1>=</span> <span class=pl-s1>os</span>.<span class=pl-c1>environ</span>.<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;CONVERSATION_TABLE_ARN&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback1-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback1-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>token_table_arn</span> <span class=pl-c1>=</span> <span class=pl-s1>os</span>.<span class=pl-c1>environ</span>.<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;TOKEN_TABLE_ARN&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback1-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback1-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback1-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback1-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Get the bot token and signing secret from AWS Secrets Manager</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback1-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback1-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Fetch secret package</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback1-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback1-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>secrets</span> <span class=pl-c1>=</span> <span class=pl-en>get_secret_ssm_layer</span>(<span class=pl-s1>bot_secret_name</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback1-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback1-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback1-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback1-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Disambiguate secrets</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback1-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback1-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>secrets_json</span> <span class=pl-c1>=</span> <span class=pl-s1>json</span>.<span class=pl-c1>loads</span>(<span class=pl-s1>secrets</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback1-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback1-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c1>TENANT_ID</span> <span class=pl-c1>=</span> <span class=pl-s1>secrets_json</span>[<span class=pl-s>&amp;quot;TENANT_ID&amp;quot;</span>]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback1-py-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback1-py-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c1>CLIENT_ID</span> <span class=pl-c1>=</span> <span class=pl-s1>secrets_json</span>[<span class=pl-s>&amp;quot;CLIENT_ID&amp;quot;</span>]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback1-py-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback1-py-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c1>CLIENT_SECRET</span> <span class=pl-c1>=</span> <span class=pl-s1>secrets_json</span>[<span class=pl-s>&amp;quot;CLIENT_SECRET&amp;quot;</span>]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback1-py-L16\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;16\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback1-py-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback1-py-L17\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;17\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback1-py-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Now we can use the bot token and signing secret</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback1-py-L18\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;18\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback1-py-LC18\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-en>print</span>(<span class=pl-s>&amp;quot;&#128994; Successfully retrieved secrets from AWS Secrets Manager&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback1-py-L19\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;19\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback1-py-LC19\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback1-py-L20\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;20\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback1-py-LC20\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Exchange the authorization code for an access token</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback1-py-L21\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;21\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback1-py-LC21\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>token_response</span> <span class=pl-c1>=</span> <span class=pl-en>exchange_code_for_token</span>(<span class=pl-s1>auth_code</span>, <span class=pl-c1>TENANT_ID</span>, <span class=pl-c1>CLIENT_ID</span>, <span class=pl-c1>CLIENT_SECRET</span>)</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/6627a988d66d6420a1f7619576136796/raw/e7a0acf5feb306e2c6cb9cda6602b89aeaf95025/handle_auth_code_callback1.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/6627a988d66d6420a1f7619576136796#file-handle_auth_code_callback1-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          handle_auth_code_callback1.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-110350e56c50.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-110350e56c50.css"><div id="gist139246533" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-handle_auth_code_callback1-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="8" data-paste-markdown-skip="" data-tagsearch-path="handle_auth_code_callback1.py">
        <tbody><tr>
          <td id="file-handle_auth_code_callback1-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-handle_auth_code_callback1-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-en">handle_auth_code_callback</span>(<span class="pl-s1">body</span>, <span class="pl-s1">event</span>, <span class="pl-s1">auth_code</span>, <span class="pl-s1">aad_object_id</span>):</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback1-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-handle_auth_code_callback1-py-LC2" class="blob-code blob-code-inner js-file-line">    </td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback1-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-handle_auth_code_callback1-py-LC3" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Read table names from environment variables</span></td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback1-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-handle_auth_code_callback1-py-LC4" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">conversation_table_arn</span> <span class="pl-c1">=</span> <span class="pl-s1">os</span>.<span class="pl-c1">environ</span>.<span class="pl-c1">get</span>(<span class="pl-s">"CONVERSATION_TABLE_ARN"</span>)</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback1-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-handle_auth_code_callback1-py-LC5" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">token_table_arn</span> <span class="pl-c1">=</span> <span class="pl-s1">os</span>.<span class="pl-c1">environ</span>.<span class="pl-c1">get</span>(<span class="pl-s">"TOKEN_TABLE_ARN"</span>)</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback1-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-handle_auth_code_callback1-py-LC6" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback1-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-handle_auth_code_callback1-py-LC7" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Get the bot token and signing secret from AWS Secrets Manager</span></td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback1-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-handle_auth_code_callback1-py-LC8" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Fetch secret package</span></td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback1-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-handle_auth_code_callback1-py-LC9" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">secrets</span> <span class="pl-c1">=</span> <span class="pl-en">get_secret_ssm_layer</span>(<span class="pl-s1">bot_secret_name</span>)</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback1-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-handle_auth_code_callback1-py-LC10" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback1-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-handle_auth_code_callback1-py-LC11" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Disambiguate secrets</span></td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback1-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-handle_auth_code_callback1-py-LC12" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">secrets_json</span> <span class="pl-c1">=</span> <span class="pl-s1">json</span>.<span class="pl-c1">loads</span>(<span class="pl-s1">secrets</span>)</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback1-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-handle_auth_code_callback1-py-LC13" class="blob-code blob-code-inner js-file-line">    <span class="pl-c1">TENANT_ID</span> <span class="pl-c1">=</span> <span class="pl-s1">secrets_json</span>[<span class="pl-s">"TENANT_ID"</span>]</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback1-py-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-handle_auth_code_callback1-py-LC14" class="blob-code blob-code-inner js-file-line">    <span class="pl-c1">CLIENT_ID</span> <span class="pl-c1">=</span> <span class="pl-s1">secrets_json</span>[<span class="pl-s">"CLIENT_ID"</span>]</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback1-py-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-handle_auth_code_callback1-py-LC15" class="blob-code blob-code-inner js-file-line">    <span class="pl-c1">CLIENT_SECRET</span> <span class="pl-c1">=</span> <span class="pl-s1">secrets_json</span>[<span class="pl-s">"CLIENT_SECRET"</span>]</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback1-py-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-handle_auth_code_callback1-py-LC16" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback1-py-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-handle_auth_code_callback1-py-LC17" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Now we can use the bot token and signing secret</span></td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback1-py-L18" class="blob-num js-line-number js-blob-rnum" data-line-number="18"></td>
          <td id="file-handle_auth_code_callback1-py-LC18" class="blob-code blob-code-inner js-file-line">    <span class="pl-en">print</span>(<span class="pl-s">"&#128994; Successfully retrieved secrets from AWS Secrets Manager"</span>)</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback1-py-L19" class="blob-num js-line-number js-blob-rnum" data-line-number="19"></td>
          <td id="file-handle_auth_code_callback1-py-LC19" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback1-py-L20" class="blob-num js-line-number js-blob-rnum" data-line-number="20"></td>
          <td id="file-handle_auth_code_callback1-py-LC20" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Exchange the authorization code for an access token</span></td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback1-py-L21" class="blob-num js-line-number js-blob-rnum" data-line-number="21"></td>
          <td id="file-handle_auth_code_callback1-py-LC21" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">token_response</span> <span class="pl-c1">=</span> <span class="pl-en">exchange_code_for_token</span>(<span class="pl-s1">auth_code</span>, <span class="pl-c1">TENANT_ID</span>, <span class="pl-c1">CLIENT_ID</span>, <span class="pl-c1">CLIENT_SECRET</span>)</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/6627a988d66d6420a1f7619576136796/raw/e7a0acf5feb306e2c6cb9cda6602b89aeaf95025/handle_auth_code_callback1.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/6627a988d66d6420a1f7619576136796#file-handle_auth_code_callback1-py" class="Link--inTextBlock">
          handle_auth_code_callback1.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><h2>Finally, The Actual Token</h2><p>Exchanging the code for a token is surprisingly straight-forward. We send over the required information, we get a token back. This token is issued for the user that&#8217;s using the bot, so we can operate as that user temporarily, which is pretty cool. </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist139246557\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-exchange_code_for_token-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;exchange_code_for_token.py content, created by KyMidd on 03:31AM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;8\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;exchange_code_for_token.py\&quot;>\n        <tr>\n          <td id=\&quot;file-exchange_code_for_token-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-exchange_code_for_token-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-en>exchange_code_for_token</span>(<span class=pl-s1>auth_code</span>, <span class=pl-s1>tenant_id</span>, <span class=pl-s1>client_id</span>, <span class=pl-s1>client_secret</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-exchange_code_for_token-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-exchange_code_for_token-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>token_url</span> <span class=pl-c1>=</span> <span class=pl-s>f&amp;quot;https://login.microsoftonline.com/<span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>tenant_id</span><span class=pl-kos>}</span></span>/oauth2/v2.0/token&amp;quot;</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-exchange_code_for_token-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-exchange_code_for_token-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>data</span> <span class=pl-c1>=</span> {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-exchange_code_for_token-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-exchange_code_for_token-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s>&amp;quot;client_id&amp;quot;</span>: <span class=pl-s1>client_id</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-exchange_code_for_token-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-exchange_code_for_token-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s>&amp;quot;client_secret&amp;quot;</span>: <span class=pl-s1>client_secret</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-exchange_code_for_token-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-exchange_code_for_token-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s>&amp;quot;scope&amp;quot;</span>: <span class=pl-s>&amp;quot;https://graph.microsoft.com/.default&amp;quot;</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-exchange_code_for_token-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-exchange_code_for_token-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s>&amp;quot;code&amp;quot;</span>: <span class=pl-s1>auth_code</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-exchange_code_for_token-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-exchange_code_for_token-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s>&amp;quot;redirect_uri&amp;quot;</span>: <span class=pl-s>f&amp;quot;<span class=pl-s1><span class=pl-kos>{</span><span class=pl-s1>redirect_function_url</span><span class=pl-kos>}</span></span>/callback&amp;quot;</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-exchange_code_for_token-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-exchange_code_for_token-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-s>&amp;quot;grant_type&amp;quot;</span>: <span class=pl-s>&amp;quot;authorization_code&amp;quot;</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-exchange_code_for_token-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-exchange_code_for_token-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    }</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-exchange_code_for_token-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-exchange_code_for_token-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-exchange_code_for_token-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-exchange_code_for_token-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>response</span> <span class=pl-c1>=</span> <span class=pl-s1>requests</span>.<span class=pl-c1>post</span>(<span class=pl-s1>token_url</span>, <span class=pl-s1>data</span><span class=pl-c1>=</span><span class=pl-s1>data</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-exchange_code_for_token-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-exchange_code_for_token-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>response</span>.<span class=pl-c1>raise_for_status</span>()</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-exchange_code_for_token-py-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-exchange_code_for_token-py-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>return</span> <span class=pl-s1>response</span>.<span class=pl-c1>json</span>()</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/fbb0d96e38c18e92d2222763f88488d9/raw/4215524fd270e08a17f5e79e52b71b277ecc429f/exchange_code_for_token.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/fbb0d96e38c18e92d2222763f88488d9#file-exchange_code_for_token-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          exchange_code_for_token.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-110350e56c50.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-110350e56c50.css"><div id="gist139246557" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-exchange_code_for_token-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="8" data-paste-markdown-skip="" data-tagsearch-path="exchange_code_for_token.py">
        <tbody><tr>
          <td id="file-exchange_code_for_token-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-exchange_code_for_token-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-en">exchange_code_for_token</span>(<span class="pl-s1">auth_code</span>, <span class="pl-s1">tenant_id</span>, <span class="pl-s1">client_id</span>, <span class="pl-s1">client_secret</span>):</td>
        </tr>
        <tr>
          <td id="file-exchange_code_for_token-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-exchange_code_for_token-py-LC2" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">token_url</span> <span class="pl-c1">=</span> <span class="pl-s">f"https://login.microsoftonline.com/<span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">tenant_id</span><span class="pl-kos">}</span></span>/oauth2/v2.0/token"</span></td>
        </tr>
        <tr>
          <td id="file-exchange_code_for_token-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-exchange_code_for_token-py-LC3" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">data</span> <span class="pl-c1">=</span> {</td>
        </tr>
        <tr>
          <td id="file-exchange_code_for_token-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-exchange_code_for_token-py-LC4" class="blob-code blob-code-inner js-file-line">        <span class="pl-s">"client_id"</span>: <span class="pl-s1">client_id</span>,</td>
        </tr>
        <tr>
          <td id="file-exchange_code_for_token-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-exchange_code_for_token-py-LC5" class="blob-code blob-code-inner js-file-line">        <span class="pl-s">"client_secret"</span>: <span class="pl-s1">client_secret</span>,</td>
        </tr>
        <tr>
          <td id="file-exchange_code_for_token-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-exchange_code_for_token-py-LC6" class="blob-code blob-code-inner js-file-line">        <span class="pl-s">"scope"</span>: <span class="pl-s">"https://graph.microsoft.com/.default"</span>,</td>
        </tr>
        <tr>
          <td id="file-exchange_code_for_token-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-exchange_code_for_token-py-LC7" class="blob-code blob-code-inner js-file-line">        <span class="pl-s">"code"</span>: <span class="pl-s1">auth_code</span>,</td>
        </tr>
        <tr>
          <td id="file-exchange_code_for_token-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-exchange_code_for_token-py-LC8" class="blob-code blob-code-inner js-file-line">        <span class="pl-s">"redirect_uri"</span>: <span class="pl-s">f"<span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">redirect_function_url</span><span class="pl-kos">}</span></span>/callback"</span>,</td>
        </tr>
        <tr>
          <td id="file-exchange_code_for_token-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-exchange_code_for_token-py-LC9" class="blob-code blob-code-inner js-file-line">        <span class="pl-s">"grant_type"</span>: <span class="pl-s">"authorization_code"</span>,</td>
        </tr>
        <tr>
          <td id="file-exchange_code_for_token-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-exchange_code_for_token-py-LC10" class="blob-code blob-code-inner js-file-line">    }</td>
        </tr>
        <tr>
          <td id="file-exchange_code_for_token-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-exchange_code_for_token-py-LC11" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-exchange_code_for_token-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-exchange_code_for_token-py-LC12" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">response</span> <span class="pl-c1">=</span> <span class="pl-s1">requests</span>.<span class="pl-c1">post</span>(<span class="pl-s1">token_url</span>, <span class="pl-s1">data</span><span class="pl-c1">=</span><span class="pl-s1">data</span>)</td>
        </tr>
        <tr>
          <td id="file-exchange_code_for_token-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-exchange_code_for_token-py-LC13" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">response</span>.<span class="pl-c1">raise_for_status</span>()</td>
        </tr>
        <tr>
          <td id="file-exchange_code_for_token-py-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-exchange_code_for_token-py-LC14" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">return</span> <span class="pl-s1">response</span>.<span class="pl-c1">json</span>()</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/fbb0d96e38c18e92d2222763f88488d9/raw/4215524fd270e08a17f5e79e52b71b277ecc429f/exchange_code_for_token.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/fbb0d96e38c18e92d2222763f88488d9#file-exchange_code_for_token-py" class="Link--inTextBlock">
          exchange_code_for_token.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><h2>Keep This Token in Your Pocket, Please</h2><p>We receive the real, actual token (PHEW), so we store it and the expiration time (remember, it&#8217;s randomized so we can&#8217;t just do math), and calculate the expiration in epic time (seconds since Jan 1 1970). </p><p>This token is sensitive, and we want to write it to dynamoDB, so we need to encrypt it. To do that, we need a KMS key, so we register the KMS client, line 15. </p><p>On line 18, we encrypt our plain-text user token using the CMK KMS key, as well as our utf-8 encoded token string. </p><p>That interestingly makes it binary, which I haven&#8217;t seen before, so we need to encode using base64 to store in dynamo, which we do on line 24. </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist139246595\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-handle_auth_code_callback2-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;handle_auth_code_callback2.py content, created by KyMidd on 03:33AM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;8\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;handle_auth_code_callback2.py\&quot;>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback2-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback2-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-en>handle_auth_code_callback</span>(<span class=pl-s1>body</span>, <span class=pl-s1>event</span>, <span class=pl-s1>auth_code</span>, <span class=pl-s1>aad_object_id</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback2-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback2-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># ...</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback2-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback2-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>token_response</span> <span class=pl-c1>=</span> <span class=pl-en>exchange_code_for_token</span>(<span class=pl-s1>auth_code</span>, <span class=pl-c1>TENANT_ID</span>, <span class=pl-c1>CLIENT_ID</span>, <span class=pl-c1>CLIENT_SECRET</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback2-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback2-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback2-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback2-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Extract the access token and expiration time</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback2-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback2-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>access_token</span> <span class=pl-c1>=</span> <span class=pl-s1>token_response</span>[<span class=pl-s>&amp;quot;access_token&amp;quot;</span>]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback2-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback2-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>expires_in</span> <span class=pl-c1>=</span> <span class=pl-s1>token_response</span>[<span class=pl-s>&amp;quot;expires_in&amp;quot;</span>]</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback2-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback2-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback2-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback2-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Calculate expiration time in seconds since epoch</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback2-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback2-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>expires_at</span> <span class=pl-c1>=</span> <span class=pl-en>int</span>(<span class=pl-s1>time</span>.<span class=pl-c1>time</span>()) <span class=pl-c1>+</span> <span class=pl-s1>expires_in</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback2-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback2-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback2-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback2-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c>### Encrypt the access token using the CMK key</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback2-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback2-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback2-py-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback2-py-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Initialize the KMS client</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback2-py-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback2-py-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>kms</span> <span class=pl-c1>=</span> <span class=pl-s1>boto3</span>.<span class=pl-c1>client</span>(<span class=pl-s>&amp;#39;kms&amp;#39;</span>, <span class=pl-s1>region_name</span><span class=pl-c1>=</span><span class=pl-s>&amp;#39;us-east-1&amp;#39;</span>)  <span class=pl-c># Change region if needed</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback2-py-L16\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;16\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback2-py-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback2-py-L17\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;17\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback2-py-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Encrypt the access token</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback2-py-L18\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;18\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback2-py-LC18\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>encrypted_token</span> <span class=pl-c1>=</span> <span class=pl-s1>kms</span>.<span class=pl-c1>encrypt</span>(</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback2-py-L19\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;19\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback2-py-LC19\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-v>KeyId</span><span class=pl-c1>=</span><span class=pl-s1>cmk_key_alias</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback2-py-L20\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;20\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback2-py-LC20\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-v>Plaintext</span><span class=pl-c1>=</span><span class=pl-s1>access_token</span>.<span class=pl-c1>encode</span>(<span class=pl-s>&amp;quot;utf-8&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback2-py-L21\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;21\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback2-py-LC21\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    )</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback2-py-L22\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;22\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback2-py-LC22\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback2-py-L23\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;23\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback2-py-LC23\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Base64 encode the encrypted token</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback2-py-L24\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;24\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback2-py-LC24\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>encrypted_token_base64</span> <span class=pl-c1>=</span> <span class=pl-s1>base64</span>.<span class=pl-c1>b64encode</span>(<span class=pl-s1>encrypted_token</span>[<span class=pl-s>&amp;#39;CiphertextBlob&amp;#39;</span>]).<span class=pl-c1>decode</span>(<span class=pl-s>&amp;#39;utf-8&amp;#39;</span>)</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/45b357841cd788f837172873bde3e7f5/raw/6544605b8c61e27eb90c33bf7c074f9aa755db56/handle_auth_code_callback2.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/45b357841cd788f837172873bde3e7f5#file-handle_auth_code_callback2-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          handle_auth_code_callback2.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-110350e56c50.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-110350e56c50.css"><div id="gist139246595" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-handle_auth_code_callback2-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="8" data-paste-markdown-skip="" data-tagsearch-path="handle_auth_code_callback2.py">
        <tbody><tr>
          <td id="file-handle_auth_code_callback2-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-handle_auth_code_callback2-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-en">handle_auth_code_callback</span>(<span class="pl-s1">body</span>, <span class="pl-s1">event</span>, <span class="pl-s1">auth_code</span>, <span class="pl-s1">aad_object_id</span>):</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback2-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-handle_auth_code_callback2-py-LC2" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># ...</span></td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback2-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-handle_auth_code_callback2-py-LC3" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">token_response</span> <span class="pl-c1">=</span> <span class="pl-en">exchange_code_for_token</span>(<span class="pl-s1">auth_code</span>, <span class="pl-c1">TENANT_ID</span>, <span class="pl-c1">CLIENT_ID</span>, <span class="pl-c1">CLIENT_SECRET</span>)</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback2-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-handle_auth_code_callback2-py-LC4" class="blob-code blob-code-inner js-file-line">    </td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback2-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-handle_auth_code_callback2-py-LC5" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Extract the access token and expiration time</span></td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback2-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-handle_auth_code_callback2-py-LC6" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">access_token</span> <span class="pl-c1">=</span> <span class="pl-s1">token_response</span>[<span class="pl-s">"access_token"</span>]</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback2-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-handle_auth_code_callback2-py-LC7" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">expires_in</span> <span class="pl-c1">=</span> <span class="pl-s1">token_response</span>[<span class="pl-s">"expires_in"</span>]</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback2-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-handle_auth_code_callback2-py-LC8" class="blob-code blob-code-inner js-file-line">    </td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback2-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-handle_auth_code_callback2-py-LC9" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Calculate expiration time in seconds since epoch</span></td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback2-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-handle_auth_code_callback2-py-LC10" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">expires_at</span> <span class="pl-c1">=</span> <span class="pl-en">int</span>(<span class="pl-s1">time</span>.<span class="pl-c1">time</span>()) <span class="pl-c1">+</span> <span class="pl-s1">expires_in</span></td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback2-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-handle_auth_code_callback2-py-LC11" class="blob-code blob-code-inner js-file-line">    </td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback2-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-handle_auth_code_callback2-py-LC12" class="blob-code blob-code-inner js-file-line">    <span class="pl-c">### Encrypt the access token using the CMK key</span></td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback2-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-handle_auth_code_callback2-py-LC13" class="blob-code blob-code-inner js-file-line">    </td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback2-py-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-handle_auth_code_callback2-py-LC14" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Initialize the KMS client</span></td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback2-py-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-handle_auth_code_callback2-py-LC15" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">kms</span> <span class="pl-c1">=</span> <span class="pl-s1">boto3</span>.<span class="pl-c1">client</span>(<span class="pl-s">'kms'</span>, <span class="pl-s1">region_name</span><span class="pl-c1">=</span><span class="pl-s">'us-east-1'</span>)  <span class="pl-c"># Change region if needed</span></td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback2-py-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-handle_auth_code_callback2-py-LC16" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback2-py-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-handle_auth_code_callback2-py-LC17" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Encrypt the access token</span></td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback2-py-L18" class="blob-num js-line-number js-blob-rnum" data-line-number="18"></td>
          <td id="file-handle_auth_code_callback2-py-LC18" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">encrypted_token</span> <span class="pl-c1">=</span> <span class="pl-s1">kms</span>.<span class="pl-c1">encrypt</span>(</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback2-py-L19" class="blob-num js-line-number js-blob-rnum" data-line-number="19"></td>
          <td id="file-handle_auth_code_callback2-py-LC19" class="blob-code blob-code-inner js-file-line">        <span class="pl-v">KeyId</span><span class="pl-c1">=</span><span class="pl-s1">cmk_key_alias</span>,</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback2-py-L20" class="blob-num js-line-number js-blob-rnum" data-line-number="20"></td>
          <td id="file-handle_auth_code_callback2-py-LC20" class="blob-code blob-code-inner js-file-line">        <span class="pl-v">Plaintext</span><span class="pl-c1">=</span><span class="pl-s1">access_token</span>.<span class="pl-c1">encode</span>(<span class="pl-s">"utf-8"</span>)</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback2-py-L21" class="blob-num js-line-number js-blob-rnum" data-line-number="21"></td>
          <td id="file-handle_auth_code_callback2-py-LC21" class="blob-code blob-code-inner js-file-line">    )</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback2-py-L22" class="blob-num js-line-number js-blob-rnum" data-line-number="22"></td>
          <td id="file-handle_auth_code_callback2-py-LC22" class="blob-code blob-code-inner js-file-line">    </td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback2-py-L23" class="blob-num js-line-number js-blob-rnum" data-line-number="23"></td>
          <td id="file-handle_auth_code_callback2-py-LC23" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Base64 encode the encrypted token</span></td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback2-py-L24" class="blob-num js-line-number js-blob-rnum" data-line-number="24"></td>
          <td id="file-handle_auth_code_callback2-py-LC24" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">encrypted_token_base64</span> <span class="pl-c1">=</span> <span class="pl-s1">base64</span>.<span class="pl-c1">b64encode</span>(<span class="pl-s1">encrypted_token</span>[<span class="pl-s">'CiphertextBlob'</span>]).<span class="pl-c1">decode</span>(<span class="pl-s">'utf-8'</span>)</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/45b357841cd788f837172873bde3e7f5/raw/6544605b8c61e27eb90c33bf7c074f9aa755db56/handle_auth_code_callback2.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/45b357841cd788f837172873bde3e7f5#file-handle_auth_code_callback2-py" class="Link--inTextBlock">
          handle_auth_code_callback2.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><h2>What Were We Talking About Again?</h2><p>Now that we have an enciphered (with KMS) and encoded (with base64) string of a token, we store it in the &#8220;token&#8221; dynamo table. All items are stored as the aadObjectId string, which we get both from the access code as well as any Teams events, so it&#8217;s a very convenient way to map stuff. </p><p>We also store the expiresAt int in the table so we can check it later. </p><p>We assume here that we ALWAYS reach here after the user has first tried to send a message, then gotten the SSO Card redirect, so we&#8217;ve stashed a conversation, so we register a dynamo_db client to go fetch the Conversation they sent initially so we can resume it, line 19. </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist139246640\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-handle_auth_code_callback_3-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;handle_auth_code_callback_3.py content, created by KyMidd on 03:38AM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;8\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;handle_auth_code_callback_3.py\&quot;>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback_3-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback_3-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-en>handle_auth_code_callback</span>(<span class=pl-s1>body</span>, <span class=pl-s1>event</span>, <span class=pl-s1>auth_code</span>, <span class=pl-s1>aad_object_id</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback_3-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback_3-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># ...</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback_3-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback_3-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>encrypted_token_base64</span> <span class=pl-c1>=</span> <span class=pl-s1>base64</span>.<span class=pl-c1>b64encode</span>(<span class=pl-s1>encrypted_token</span>[<span class=pl-s>&amp;#39;CiphertextBlob&amp;#39;</span>]).<span class=pl-c1>decode</span>(<span class=pl-s>&amp;#39;utf-8&amp;#39;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback_3-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback_3-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback_3-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback_3-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Store the access token in DynamoDB, can be used in future transactions for an hour (default expiration)</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback_3-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback_3-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>dynamodb_client</span> <span class=pl-c1>=</span> <span class=pl-s1>boto3</span>.<span class=pl-c1>client</span>(<span class=pl-s>&amp;quot;dynamodb&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback_3-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback_3-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>dynamodb_client</span>.<span class=pl-c1>put_item</span>(</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback_3-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback_3-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-v>TableName</span><span class=pl-c1>=</span><span class=pl-s1>token_table_arn</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback_3-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback_3-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-v>Item</span><span class=pl-c1>=</span>{</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback_3-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback_3-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s>&amp;quot;aadObjectId&amp;quot;</span>: {<span class=pl-s>&amp;quot;S&amp;quot;</span>: <span class=pl-s1>aad_object_id</span>},</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback_3-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback_3-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s>&amp;quot;accessToken&amp;quot;</span>: {<span class=pl-s>&amp;quot;S&amp;quot;</span>: <span class=pl-s1>encrypted_token_base64</span>},</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback_3-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback_3-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s>&amp;quot;expiresAt&amp;quot;</span>: {<span class=pl-s>&amp;quot;N&amp;quot;</span>: <span class=pl-en>str</span>(<span class=pl-s1>expires_at</span>)}</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback_3-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback_3-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        }</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback_3-py-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback_3-py-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    )</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback_3-py-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback_3-py-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback_3-py-L16\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;16\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback_3-py-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-en>print</span>(<span class=pl-s>&amp;quot;&#128994; Successfully stored access token in DynamoDB&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback_3-py-L17\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;17\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback_3-py-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback_3-py-L18\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;18\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback_3-py-LC18\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Fetch the conversation body from DynamoDB</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback_3-py-L19\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;19\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback_3-py-LC19\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>response</span> <span class=pl-c1>=</span> <span class=pl-s1>dynamodb_client</span>.<span class=pl-c1>get_item</span>(</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback_3-py-L20\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;20\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback_3-py-LC20\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-v>TableName</span><span class=pl-c1>=</span><span class=pl-s1>conversation_table_arn</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback_3-py-L21\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;21\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback_3-py-LC21\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-v>Key</span><span class=pl-c1>=</span>{</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback_3-py-L22\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;22\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback_3-py-LC22\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s>&amp;quot;aadObjectId&amp;quot;</span>: {<span class=pl-s>&amp;quot;S&amp;quot;</span>: <span class=pl-s1>aad_object_id</span>}</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback_3-py-L23\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;23\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback_3-py-LC23\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        }</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback_3-py-L24\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;24\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback_3-py-LC24\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    )</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/c153a02ec89900e532e33fad002afc6f/raw/cae374ee085f7b140f05fd9e4718309f6ea0205c/handle_auth_code_callback_3.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/c153a02ec89900e532e33fad002afc6f#file-handle_auth_code_callback_3-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          handle_auth_code_callback_3.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-110350e56c50.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-110350e56c50.css"><div id="gist139246640" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-handle_auth_code_callback_3-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="8" data-paste-markdown-skip="" data-tagsearch-path="handle_auth_code_callback_3.py">
        <tbody><tr>
          <td id="file-handle_auth_code_callback_3-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-handle_auth_code_callback_3-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-en">handle_auth_code_callback</span>(<span class="pl-s1">body</span>, <span class="pl-s1">event</span>, <span class="pl-s1">auth_code</span>, <span class="pl-s1">aad_object_id</span>):</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback_3-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-handle_auth_code_callback_3-py-LC2" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># ...</span></td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback_3-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-handle_auth_code_callback_3-py-LC3" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">encrypted_token_base64</span> <span class="pl-c1">=</span> <span class="pl-s1">base64</span>.<span class="pl-c1">b64encode</span>(<span class="pl-s1">encrypted_token</span>[<span class="pl-s">'CiphertextBlob'</span>]).<span class="pl-c1">decode</span>(<span class="pl-s">'utf-8'</span>)</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback_3-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-handle_auth_code_callback_3-py-LC4" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback_3-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-handle_auth_code_callback_3-py-LC5" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Store the access token in DynamoDB, can be used in future transactions for an hour (default expiration)</span></td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback_3-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-handle_auth_code_callback_3-py-LC6" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">dynamodb_client</span> <span class="pl-c1">=</span> <span class="pl-s1">boto3</span>.<span class="pl-c1">client</span>(<span class="pl-s">"dynamodb"</span>)</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback_3-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-handle_auth_code_callback_3-py-LC7" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">dynamodb_client</span>.<span class="pl-c1">put_item</span>(</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback_3-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-handle_auth_code_callback_3-py-LC8" class="blob-code blob-code-inner js-file-line">        <span class="pl-v">TableName</span><span class="pl-c1">=</span><span class="pl-s1">token_table_arn</span>,</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback_3-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-handle_auth_code_callback_3-py-LC9" class="blob-code blob-code-inner js-file-line">        <span class="pl-v">Item</span><span class="pl-c1">=</span>{</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback_3-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-handle_auth_code_callback_3-py-LC10" class="blob-code blob-code-inner js-file-line">            <span class="pl-s">"aadObjectId"</span>: {<span class="pl-s">"S"</span>: <span class="pl-s1">aad_object_id</span>},</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback_3-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-handle_auth_code_callback_3-py-LC11" class="blob-code blob-code-inner js-file-line">            <span class="pl-s">"accessToken"</span>: {<span class="pl-s">"S"</span>: <span class="pl-s1">encrypted_token_base64</span>},</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback_3-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-handle_auth_code_callback_3-py-LC12" class="blob-code blob-code-inner js-file-line">            <span class="pl-s">"expiresAt"</span>: {<span class="pl-s">"N"</span>: <span class="pl-en">str</span>(<span class="pl-s1">expires_at</span>)}</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback_3-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-handle_auth_code_callback_3-py-LC13" class="blob-code blob-code-inner js-file-line">        }</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback_3-py-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-handle_auth_code_callback_3-py-LC14" class="blob-code blob-code-inner js-file-line">    )</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback_3-py-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-handle_auth_code_callback_3-py-LC15" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback_3-py-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-handle_auth_code_callback_3-py-LC16" class="blob-code blob-code-inner js-file-line">    <span class="pl-en">print</span>(<span class="pl-s">"&#128994; Successfully stored access token in DynamoDB"</span>)</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback_3-py-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-handle_auth_code_callback_3-py-LC17" class="blob-code blob-code-inner js-file-line">    </td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback_3-py-L18" class="blob-num js-line-number js-blob-rnum" data-line-number="18"></td>
          <td id="file-handle_auth_code_callback_3-py-LC18" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Fetch the conversation body from DynamoDB</span></td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback_3-py-L19" class="blob-num js-line-number js-blob-rnum" data-line-number="19"></td>
          <td id="file-handle_auth_code_callback_3-py-LC19" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">response</span> <span class="pl-c1">=</span> <span class="pl-s1">dynamodb_client</span>.<span class="pl-c1">get_item</span>(</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback_3-py-L20" class="blob-num js-line-number js-blob-rnum" data-line-number="20"></td>
          <td id="file-handle_auth_code_callback_3-py-LC20" class="blob-code blob-code-inner js-file-line">        <span class="pl-v">TableName</span><span class="pl-c1">=</span><span class="pl-s1">conversation_table_arn</span>,</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback_3-py-L21" class="blob-num js-line-number js-blob-rnum" data-line-number="21"></td>
          <td id="file-handle_auth_code_callback_3-py-LC21" class="blob-code blob-code-inner js-file-line">        <span class="pl-v">Key</span><span class="pl-c1">=</span>{</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback_3-py-L22" class="blob-num js-line-number js-blob-rnum" data-line-number="22"></td>
          <td id="file-handle_auth_code_callback_3-py-LC22" class="blob-code blob-code-inner js-file-line">            <span class="pl-s">"aadObjectId"</span>: {<span class="pl-s">"S"</span>: <span class="pl-s1">aad_object_id</span>}</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback_3-py-L23" class="blob-num js-line-number js-blob-rnum" data-line-number="23"></td>
          <td id="file-handle_auth_code_callback_3-py-LC23" class="blob-code blob-code-inner js-file-line">        }</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback_3-py-L24" class="blob-num js-line-number js-blob-rnum" data-line-number="24"></td>
          <td id="file-handle_auth_code_callback_3-py-LC24" class="blob-code blob-code-inner js-file-line">    )</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/c153a02ec89900e532e33fad002afc6f/raw/cae374ee085f7b140f05fd9e4718309f6ea0205c/handle_auth_code_callback_3.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/c153a02ec89900e532e33fad002afc6f#file-handle_auth_code_callback_3-py" class="Link--inTextBlock">
          handle_auth_code_callback_3.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><h2>Wake up Worker Bot</h2><p>Once we get the conversation event, we hydrate it (line 3). </p><p>We&#8217;ll want to pass this Conversation payload, as well as the token, to our Worker lambda, so we add the encrypted and encoded token to the payload, line 6. </p><p>Now we&#8217;re ready to launch the worker, so we register a client (line 9), and trigger the lambda worker, line 12. </p><p>If this works properly, the Worker is now responding, so we delete the saved Conversation item from the table for cleanup, line 23. </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist139246672\&quot; class=\&quot;gist\&quot;>\n    <div class=\&quot;gist-file\&quot; translate=\&quot;no\&quot; data-color-mode=\&quot;light\&quot; data-light-theme=\&quot;light\&quot;>\n      <div class=\&quot;gist-data\&quot;>\n        <div class=\&quot;js-gist-file-update-container js-task-list-container\&quot;>\n  <div id=\&quot;file-handle_auth_code_callback_4-py\&quot; class=\&quot;file my-2\&quot;>\n    \n    <div itemprop=\&quot;text\&quot;\n      class=\&quot;Box-body p-0 blob-wrapper data type-python  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;handle_auth_code_callback_4.py content, created by KyMidd on 03:41AM today.\&quot;\n    >\n\n        \n<div class=\&quot;js-check-hidden-unicode js-blob-code-container blob-code-content\&quot;>\n\n  <template class=\&quot;js-file-alert-template\&quot;>\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash flash-warn flash-full d-flex flex-items-center\&quot;>\n  <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n    <span>\n      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.co/hiddenchars\&quot; target=\&quot;_blank\&quot;>Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\&quot;true\&quot; class=\&quot;flash-action\&quot;>        <a href=\&quot;{{ revealButtonHref }}\&quot; data-view-component=\&quot;true\&quot; class=\&quot;btn-sm btn\&quot;>    Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\&quot;js-line-alert-template\&quot;>\n  <span aria-label=\&quot;This line has hidden Unicode characters\&quot; data-view-component=\&quot;true\&quot; class=\&quot;line-alert tooltipped tooltipped-e\&quot;>\n    <svg aria-hidden=\&quot;true\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 16 16\&quot; version=\&quot;1.1\&quot; width=\&quot;16\&quot; data-view-component=\&quot;true\&quot; class=\&quot;octicon octicon-alert\&quot;>\n    <path d=\&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\&quot;></path>\n</svg>\n</span></template>\n\n  <table data-hpc class=\&quot;highlight tab-size js-file-line-container\&quot; data-tab-size=\&quot;8\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;handle_auth_code_callback_4.py\&quot;>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-L1\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;1\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=pl-k>def</span> <span class=pl-en>handle_auth_code_callback</span>(<span class=pl-s1>body</span>, <span class=pl-s1>event</span>, <span class=pl-s1>auth_code</span>, <span class=pl-s1>aad_object_id</span>):</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-L2\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;2\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># ...</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-L3\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;3\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>conversation_event</span> <span class=pl-c1>=</span> <span class=pl-s1>json</span>.<span class=pl-c1>loads</span>(<span class=pl-s1>response</span>.<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;Item&amp;quot;</span>)[<span class=pl-s>&amp;quot;event&amp;quot;</span>][<span class=pl-s>&amp;quot;S&amp;quot;</span>])</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-L4\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;4\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-L5\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;5\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Add the token to the event</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-L6\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;6\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>conversation_event</span>[<span class=pl-s>&amp;quot;token&amp;quot;</span>] <span class=pl-c1>=</span> <span class=pl-s1>encrypted_token_base64</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-L7\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;7\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-L8\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;8\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Trigger the Vera Worker lambda to process the event</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-L9\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;9\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>lambda_client</span> <span class=pl-c1>=</span> <span class=pl-s1>boto3</span>.<span class=pl-c1>client</span>(<span class=pl-s>&amp;#39;lambda&amp;#39;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-L10\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;10\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-L11\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;11\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Asynchronously invoke the processor Lambda</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-L12\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;12\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>lambda_client</span>.<span class=pl-c1>invoke</span>(</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-L13\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;13\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-v>FunctionName</span><span class=pl-c1>=</span><span class=pl-s1>os</span>.<span class=pl-c1>environ</span>[<span class=pl-s>&amp;#39;WORKER_LAMBDA_NAME&amp;#39;</span>],</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-L14\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;14\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-v>InvocationType</span><span class=pl-c1>=</span><span class=pl-s>&amp;#39;Event&amp;#39;</span>,  <span class=pl-c># Async invocation</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-L15\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;15\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-v>Payload</span><span class=pl-c1>=</span><span class=pl-s1>json</span>.<span class=pl-c1>dumps</span>(<span class=pl-s1>conversation_event</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-L16\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;16\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    )</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-L17\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;17\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-L18\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;18\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-LC18\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Debug</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-L19\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;19\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-LC19\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-k>if</span> <span class=pl-s1>os</span>.<span class=pl-c1>environ</span>.<span class=pl-c1>get</span>(<span class=pl-s>&amp;quot;VERA_DEBUG&amp;quot;</span>) <span class=pl-c1>==</span> <span class=pl-s>&amp;quot;True&amp;quot;</span>:</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-L20\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;20\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-LC20\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>      <span class=pl-en>print</span>(<span class=pl-s>&amp;quot;&#128994; Successfully invoked the processor Lambda with the access token&amp;quot;</span>)</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-L21\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;21\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-LC21\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    </td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-L22\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;22\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-LC22\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-c># Delete the saved conversation event from DynamoDB</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-L23\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;23\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-LC23\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=pl-s1>dynamodb_client</span>.<span class=pl-c1>delete_item</span>(</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-L24\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;24\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-LC24\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-v>TableName</span><span class=pl-c1>=</span><span class=pl-s1>conversation_table_arn</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-L25\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;25\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-LC25\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=pl-v>Key</span><span class=pl-c1>=</span>{</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-L26\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;26\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-LC26\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>            <span class=pl-s>&amp;quot;aadObjectId&amp;quot;</span>: {<span class=pl-s>&amp;quot;S&amp;quot;</span>: <span class=pl-s1>aad_object_id</span>}</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-L27\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;27\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-LC27\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        }</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-L28\&quot; class=\&quot;blob-num js-line-number js-blob-rnum\&quot; data-line-number=\&quot;28\&quot;></td>\n          <td id=\&quot;file-handle_auth_code_callback_4-py-LC28\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    )</td>\n        </tr>\n  </table>\n</div>\n\n\n    </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\&quot;gist-meta\&quot;>\n        <a href=\&quot;https://gist.github.com/KyMidd/0485d1b208230cb11fdf881cdcaa872b/raw/f6de5a01ec337c8f0418247e29193f0c8e0dd690/handle_auth_code_callback_4.py\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/0485d1b208230cb11fdf881cdcaa872b#file-handle_auth_code_callback_4-py\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          handle_auth_code_callback_4.py\n        </a>\n        hosted with &amp;#10084; by <a class=\&quot;Link--inTextBlock\&quot; href=\&quot;https://github.com\&quot;>GitHub</a>\n      </div>\n    </div>\n</div>\n&quot;,&quot;stylesheet&quot;:&quot;https://github.githubassets.com/assets/gist-embed-110350e56c50.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-110350e56c50.css"><div id="gist139246672" class="gist">
    <div class="gist-file" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        <div class="js-gist-file-update-container js-task-list-container">
  <div id="file-handle_auth_code_callback_4-py" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-python  " style="overflow:auto">

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  
    

    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div>

  <span data-view-component="true" class="line-alert tooltipped tooltipped-e">
    
    

</span>

  <table data-hpc="" class="highlight tab-size js-file-line-container" data-tab-size="8" data-paste-markdown-skip="" data-tagsearch-path="handle_auth_code_callback_4.py">
        <tbody><tr>
          <td id="file-handle_auth_code_callback_4-py-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-handle_auth_code_callback_4-py-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-k">def</span> <span class="pl-en">handle_auth_code_callback</span>(<span class="pl-s1">body</span>, <span class="pl-s1">event</span>, <span class="pl-s1">auth_code</span>, <span class="pl-s1">aad_object_id</span>):</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback_4-py-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-handle_auth_code_callback_4-py-LC2" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># ...</span></td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback_4-py-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-handle_auth_code_callback_4-py-LC3" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">conversation_event</span> <span class="pl-c1">=</span> <span class="pl-s1">json</span>.<span class="pl-c1">loads</span>(<span class="pl-s1">response</span>.<span class="pl-c1">get</span>(<span class="pl-s">"Item"</span>)[<span class="pl-s">"event"</span>][<span class="pl-s">"S"</span>])</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback_4-py-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-handle_auth_code_callback_4-py-LC4" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback_4-py-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-handle_auth_code_callback_4-py-LC5" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Add the token to the event</span></td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback_4-py-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-handle_auth_code_callback_4-py-LC6" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">conversation_event</span>[<span class="pl-s">"token"</span>] <span class="pl-c1">=</span> <span class="pl-s1">encrypted_token_base64</span></td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback_4-py-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-handle_auth_code_callback_4-py-LC7" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback_4-py-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-handle_auth_code_callback_4-py-LC8" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Trigger the Vera Worker lambda to process the event</span></td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback_4-py-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-handle_auth_code_callback_4-py-LC9" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">lambda_client</span> <span class="pl-c1">=</span> <span class="pl-s1">boto3</span>.<span class="pl-c1">client</span>(<span class="pl-s">'lambda'</span>)</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback_4-py-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-handle_auth_code_callback_4-py-LC10" class="blob-code blob-code-inner js-file-line">    </td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback_4-py-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-handle_auth_code_callback_4-py-LC11" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Asynchronously invoke the processor Lambda</span></td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback_4-py-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-handle_auth_code_callback_4-py-LC12" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">lambda_client</span>.<span class="pl-c1">invoke</span>(</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback_4-py-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-handle_auth_code_callback_4-py-LC13" class="blob-code blob-code-inner js-file-line">        <span class="pl-v">FunctionName</span><span class="pl-c1">=</span><span class="pl-s1">os</span>.<span class="pl-c1">environ</span>[<span class="pl-s">'WORKER_LAMBDA_NAME'</span>],</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback_4-py-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-handle_auth_code_callback_4-py-LC14" class="blob-code blob-code-inner js-file-line">        <span class="pl-v">InvocationType</span><span class="pl-c1">=</span><span class="pl-s">'Event'</span>,  <span class="pl-c"># Async invocation</span></td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback_4-py-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-handle_auth_code_callback_4-py-LC15" class="blob-code blob-code-inner js-file-line">        <span class="pl-v">Payload</span><span class="pl-c1">=</span><span class="pl-s1">json</span>.<span class="pl-c1">dumps</span>(<span class="pl-s1">conversation_event</span>)</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback_4-py-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-handle_auth_code_callback_4-py-LC16" class="blob-code blob-code-inner js-file-line">    )</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback_4-py-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-handle_auth_code_callback_4-py-LC17" class="blob-code blob-code-inner js-file-line">    </td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback_4-py-L18" class="blob-num js-line-number js-blob-rnum" data-line-number="18"></td>
          <td id="file-handle_auth_code_callback_4-py-LC18" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Debug</span></td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback_4-py-L19" class="blob-num js-line-number js-blob-rnum" data-line-number="19"></td>
          <td id="file-handle_auth_code_callback_4-py-LC19" class="blob-code blob-code-inner js-file-line">    <span class="pl-k">if</span> <span class="pl-s1">os</span>.<span class="pl-c1">environ</span>.<span class="pl-c1">get</span>(<span class="pl-s">"VERA_DEBUG"</span>) <span class="pl-c1">==</span> <span class="pl-s">"True"</span>:</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback_4-py-L20" class="blob-num js-line-number js-blob-rnum" data-line-number="20"></td>
          <td id="file-handle_auth_code_callback_4-py-LC20" class="blob-code blob-code-inner js-file-line">      <span class="pl-en">print</span>(<span class="pl-s">"&#128994; Successfully invoked the processor Lambda with the access token"</span>)</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback_4-py-L21" class="blob-num js-line-number js-blob-rnum" data-line-number="21"></td>
          <td id="file-handle_auth_code_callback_4-py-LC21" class="blob-code blob-code-inner js-file-line">    </td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback_4-py-L22" class="blob-num js-line-number js-blob-rnum" data-line-number="22"></td>
          <td id="file-handle_auth_code_callback_4-py-LC22" class="blob-code blob-code-inner js-file-line">    <span class="pl-c"># Delete the saved conversation event from DynamoDB</span></td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback_4-py-L23" class="blob-num js-line-number js-blob-rnum" data-line-number="23"></td>
          <td id="file-handle_auth_code_callback_4-py-LC23" class="blob-code blob-code-inner js-file-line">    <span class="pl-s1">dynamodb_client</span>.<span class="pl-c1">delete_item</span>(</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback_4-py-L24" class="blob-num js-line-number js-blob-rnum" data-line-number="24"></td>
          <td id="file-handle_auth_code_callback_4-py-LC24" class="blob-code blob-code-inner js-file-line">        <span class="pl-v">TableName</span><span class="pl-c1">=</span><span class="pl-s1">conversation_table_arn</span>,</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback_4-py-L25" class="blob-num js-line-number js-blob-rnum" data-line-number="25"></td>
          <td id="file-handle_auth_code_callback_4-py-LC25" class="blob-code blob-code-inner js-file-line">        <span class="pl-v">Key</span><span class="pl-c1">=</span>{</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback_4-py-L26" class="blob-num js-line-number js-blob-rnum" data-line-number="26"></td>
          <td id="file-handle_auth_code_callback_4-py-LC26" class="blob-code blob-code-inner js-file-line">            <span class="pl-s">"aadObjectId"</span>: {<span class="pl-s">"S"</span>: <span class="pl-s1">aad_object_id</span>}</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback_4-py-L27" class="blob-num js-line-number js-blob-rnum" data-line-number="27"></td>
          <td id="file-handle_auth_code_callback_4-py-LC27" class="blob-code blob-code-inner js-file-line">        }</td>
        </tr>
        <tr>
          <td id="file-handle_auth_code_callback_4-py-L28" class="blob-num js-line-number js-blob-rnum" data-line-number="28"></td>
          <td id="file-handle_auth_code_callback_4-py-LC28" class="blob-code blob-code-inner js-file-line">    )</td>
        </tr>
  </tbody></table>
</div>


    </div>

  </div>
</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/KyMidd/0485d1b208230cb11fdf881cdcaa872b/raw/f6de5a01ec337c8f0418247e29193f0c8e0dd690/handle_auth_code_callback_4.py" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/0485d1b208230cb11fdf881cdcaa872b#file-handle_auth_code_callback_4-py" class="Link--inTextBlock">
          handle_auth_code_callback_4.py
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>And that&#8217;s it! </p><h1>Summary</h1><p>This got WAY LONGER than I planned. There&#8217;s a lot of nuance in how Entra IdP works to issue tokens, how lambda and dynamoDB work to store and process data, and in how python can route traffic in this Receiver pattern. </p><p>I&#8217;m particularly proud of the &#8220;automatically open Teams after you authenticate&#8221; part, I find that really cool in practice. </p><p>Hopefully you have a pretty solid idea of how this pattern works and how to customize it for your user case. </p><p>In the next article we&#8217;ll go over some of the Worker items for how we look up items and respond to conversations - it&#8217;s awesome, and entirely different from Slack. </p><p>Until next time. Good luck out there! <br>kyler</p>]]></content:encoded></item></channel></rss>