<?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>Wed, 29 Apr 2026 08:28:41 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[🔥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>
      <p>
          <a href="https://www.letsdodevops.com/p/agentcore-deployment-operations-and">
              Read more
          </a>
      </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>
      <p>
          <a href="https://www.letsdodevops.com/p/aws-agentcore-agentic-slack-bot-agentcore">
              Read more
          </a>
      </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>
      <p>
          <a href="https://www.letsdodevops.com/p/aws-agentcore-agentic-slack-bot-full-a98">
              Read more
          </a>
      </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><item><title><![CDATA[🔥Building a Teams Bot with AI Capabilities - Part 3 - Delegated Permissions and Making Lambda Stateful for Oauth2🔥]]></title><description><![CDATA[aka, "do you remember me?"]]></description><link>https://www.letsdodevops.com/p/teamsai-3</link><guid isPermaLink="false">https://www.letsdodevops.com/p/teamsai-3</guid><dc:creator><![CDATA[Kyler Middleton]]></dc:creator><pubDate>Tue, 17 Jun 2025 12:02:50 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!94Gd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5d88a1d-7052-44a9-b0b9-fa9e2d11e315_576x343.gif" 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 (this article): Delegated Permissions and Making Lambda Stateful for Oauth2</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: <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>During the last two articles, we talked about how to get started building a Teams Bot - we built the manifest, registered the Bot resource, and linked it to an App Registration. That App Registration contains all sorts of wonderful permissions that we need to use in order to build conversation context and operate as a bot. </p><p>However, all the permissions are set as Delegated - that means that the Bot can&#8217;t do those things itself - it has no rights at all, since all the permissions are &#8220;Delegated&#8221; (vs &#8220;Application&#8221; permissions). </p><p>I talked to our Azure admin about just granting the Bot Application permissions to: </p><ul><li><p>Read all Channels</p></li><li><p>Read all Messages in any channel</p></li><li><p>Read all private chats</p></li></ul><p>And he just laughed and laughed. Granting a static permission to a bot to work like that would be bizzare - that&#8217;s way too many permissions! And if someone was able to steal the Client ID and Client Secret, they could exfiltrate absolutely all data from our Teams. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!94Gd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5d88a1d-7052-44a9-b0b9-fa9e2d11e315_576x343.gif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!94Gd!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5d88a1d-7052-44a9-b0b9-fa9e2d11e315_576x343.gif 424w, https://substackcdn.com/image/fetch/$s_!94Gd!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5d88a1d-7052-44a9-b0b9-fa9e2d11e315_576x343.gif 848w, https://substackcdn.com/image/fetch/$s_!94Gd!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5d88a1d-7052-44a9-b0b9-fa9e2d11e315_576x343.gif 1272w, https://substackcdn.com/image/fetch/$s_!94Gd!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5d88a1d-7052-44a9-b0b9-fa9e2d11e315_576x343.gif 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!94Gd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5d88a1d-7052-44a9-b0b9-fa9e2d11e315_576x343.gif" width="576" height="343" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d5d88a1d-7052-44a9-b0b9-fa9e2d11e315_576x343.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:343,&quot;width&quot;:576,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1546947,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/gif&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.letsdodevops.com/i/165677362?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5d88a1d-7052-44a9-b0b9-fa9e2d11e315_576x343.gif&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!94Gd!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5d88a1d-7052-44a9-b0b9-fa9e2d11e315_576x343.gif 424w, https://substackcdn.com/image/fetch/$s_!94Gd!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5d88a1d-7052-44a9-b0b9-fa9e2d11e315_576x343.gif 848w, https://substackcdn.com/image/fetch/$s_!94Gd!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5d88a1d-7052-44a9-b0b9-fa9e2d11e315_576x343.gif 1272w, https://substackcdn.com/image/fetch/$s_!94Gd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5d88a1d-7052-44a9-b0b9-fa9e2d11e315_576x343.gif 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>Thus, delegated permissions. </p><p>Lets talk about what delegated permissions are, and then talk about how the changes we need to make to our Receiver lambda (that is obviously stateless, it&#8217;s a lambda), to operate in a stateful way. </p><blockquote><p>Don&#8217;t understand why our Receiver lambda needs state? Well, read on! It&#8217;s all about the OAuth2 token delivery safety mechanism in Azure</p></blockquote><p>If you&#8217;d rather skip right to the code, <a href="https://github.com/KyMidd/TeamsAIBot">this tool is available and open source</a>. Please, go build! </p><h1>Oauth2 Tokens in Delegated Access - Theory</h1><p>So, Delegated Permissions. That&#8217;s cool, I don&#8217;t care if we use a User&#8217;s token or the App&#8217;s token, right? The flow is a little different, and we&#8217;ll call out an item that makes a pretty big change for us. </p><p>First of all, Users have to <strong>do something</strong> to issue a token. They&#8217;ll need to visit an SSO page for your organization and click an account to login as. Teams makes that pretty easy using a &#8220;Card&#8221; with a button, that a user will need to click in order to take them to the SSO page - that page is hosted by Microsoft (or your 3rd party SSO provider). </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!NGdU!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d8d4b3a-e161-4b85-94cd-d33f22e6f4ab_1668x770.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!NGdU!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d8d4b3a-e161-4b85-94cd-d33f22e6f4ab_1668x770.png 424w, https://substackcdn.com/image/fetch/$s_!NGdU!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d8d4b3a-e161-4b85-94cd-d33f22e6f4ab_1668x770.png 848w, https://substackcdn.com/image/fetch/$s_!NGdU!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d8d4b3a-e161-4b85-94cd-d33f22e6f4ab_1668x770.png 1272w, https://substackcdn.com/image/fetch/$s_!NGdU!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d8d4b3a-e161-4b85-94cd-d33f22e6f4ab_1668x770.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!NGdU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d8d4b3a-e161-4b85-94cd-d33f22e6f4ab_1668x770.png" width="1456" height="672" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0d8d4b3a-e161-4b85-94cd-d33f22e6f4ab_1668x770.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:672,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:114861,&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/165677362?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d8d4b3a-e161-4b85-94cd-d33f22e6f4ab_1668x770.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_!NGdU!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d8d4b3a-e161-4b85-94cd-d33f22e6f4ab_1668x770.png 424w, https://substackcdn.com/image/fetch/$s_!NGdU!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d8d4b3a-e161-4b85-94cd-d33f22e6f4ab_1668x770.png 848w, https://substackcdn.com/image/fetch/$s_!NGdU!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d8d4b3a-e161-4b85-94cd-d33f22e6f4ab_1668x770.png 1272w, https://substackcdn.com/image/fetch/$s_!NGdU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d8d4b3a-e161-4b85-94cd-d33f22e6f4ab_1668x770.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>Now, when we do that, we don&#8217;t magically get a token. Instead, the Microsoft App Registration resource, and the Microsoft token issuing infrastructure, POSTs a token to a permitted URL. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!4YE-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb318c096-12f2-4f88-8308-f4b9c9736970_2018x1140.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!4YE-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb318c096-12f2-4f88-8308-f4b9c9736970_2018x1140.png 424w, https://substackcdn.com/image/fetch/$s_!4YE-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb318c096-12f2-4f88-8308-f4b9c9736970_2018x1140.png 848w, https://substackcdn.com/image/fetch/$s_!4YE-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb318c096-12f2-4f88-8308-f4b9c9736970_2018x1140.png 1272w, https://substackcdn.com/image/fetch/$s_!4YE-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb318c096-12f2-4f88-8308-f4b9c9736970_2018x1140.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!4YE-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb318c096-12f2-4f88-8308-f4b9c9736970_2018x1140.png" width="1456" height="823" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b318c096-12f2-4f88-8308-f4b9c9736970_2018x1140.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:823,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:315995,&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/165677362?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb318c096-12f2-4f88-8308-f4b9c9736970_2018x1140.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_!4YE-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb318c096-12f2-4f88-8308-f4b9c9736970_2018x1140.png 424w, https://substackcdn.com/image/fetch/$s_!4YE-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb318c096-12f2-4f88-8308-f4b9c9736970_2018x1140.png 848w, https://substackcdn.com/image/fetch/$s_!4YE-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb318c096-12f2-4f88-8308-f4b9c9736970_2018x1140.png 1272w, https://substackcdn.com/image/fetch/$s_!4YE-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb318c096-12f2-4f88-8308-f4b9c9736970_2018x1140.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>Do you see the issue yet? We&#8217;re running a LAMBDA, that executes once for every inbound connection, and in order to use a Delegated Token, we need to have a SECOND inbound connection that carries the token. </p><blockquote><p>That fundamentally will not work in a stateless implementation</p></blockquote><p>So we need to build some state - for two distinct purposes. </p><h2>Stateful &#8220;Conversation&#8221; DynamoDB Table</h2><p>First, when we receive the initial webhook, it contains the &#8220;tag&#8221; message where the user triggered the Bot. If we don&#8217;t have a token available for the user, or if that token is expired, we know this iteration of our lambda can&#8217;t proceed, but we need to store the conversation for the next run, when the user hopefully authenticates to SSO and sends a token to our lambda. </p><p>These conversations aren&#8217;t particularly sensitive - they contain the user&#8217;s AzureAD/Entra object ID, and the text of the message that triggered the bot, but documents are linked, and any contextual conversation information isn&#8217;t included, so there&#8217;s no real impetus to encrypt this, but you do you if it gives you good security feels. </p><p>This storage operates like a queue - we store it and then when receiving the token, we &#8220;process&#8221; it and immediately delete it after using. </p><h2>Stateful &#8220;Token&#8221; DynamoDB Table</h2><p>Second, we don&#8217;t want the user to have to SSO for every single message. That&#8217;d be crazy, especially because the token is valid for around an hour (<a href="https://learn.microsoft.com/en-us/entra/identity-platform/access-tokens#token-lifetime">60-90 minutes, randomly</a>). So when we receive a new token, we need to store it in a stateful way, so we can use it on the next triggering from that user. </p><p>When a user triggers the bot, we need to read the list of stored tokens to see if any match the user that&#8217;s triggered the bot. If yes, we&#8217;ll check if they&#8217;re expired, and if they are, we throw it away and push the &#8220;card&#8221; to the user so they can authorize a new token. </p><p>Tokens are particularly sensitive - they let anyone who obtains it to operate as if they are the user, and do any of the authorized actions, which in this case is to read their private messages and all user data for channels they&#8217;re in - not ideal. So we need to encrypt this data. I implemented encryption using a KMS CMK (an AWS-hosted Client Managed Key (CMK) from the Key Management Service (KMS)). This means that the data is strongly encrypted - AES-256-GCM, suitable for HIPAA, HITECH, and FISMA/FedRamp. </p><p>I want to be as efficient as possible to operate quickly since the user is probably waiting on us in their Teams app, so we pack the encrypted token and the conversation trigger the user sent (that triggered the bot), to the Worker lambda. I generally log the handoff between the Receiver layer (that receives conversation triggers and stores tokens) and the Worker layer (that builds the context and processes all the AI conversations), so we make sure the token is always encrypted at that handoff, and the Worker needs to access the KMS CMK again to decrypt the token to plaintext. </p><p>The token is never culled asynchronously - it&#8217;s only ever deleted when the following are satisfied: </p><ul><li><p>The user interacts with the bot</p></li><li><p>The Receiver lambda finds the stored token is expired</p></li></ul><p>In that case, the Receiver deletes the stored token for that particular user (primary key is the user&#8217;s AzureAD/Entra ID), and pushes a Card to the user so they can authorize a new token. </p><h2>Routing Logic</h2><p>You&#8217;ll notice there are a few different flows here. </p><p>If the user has never interacted with the bot before, or if the token is expired, we push a Card so the user can authorize a new Token issuance. On the SSO Token&#8217;s receipt, we store it in the dynamoDB Token table, fetch the stored Conversation from the table, and trigger the Worker. </p><p>If the user&#8217;s token is present and valid, we fetch the user&#8217;s encrypted Token, pack it with the Conversation webhook directly, and trigger the Worker. </p><p>Notably, the Receiver handles all this logic. The Worker is only triggered when we&#8217;ve validated that all the requisite data is populated for us to work the conversation. </p><h1>Lets Code It</h1><p>Okay, theory is well and good, but lets build it. </p><p>First, lets build the Conversation table. This caches Conversations if there&#8217;s an invalid or missing Token from the user. We set this as a small, low volume table (lines 9 and 10 set the compute units). </p><p>We set the &#8220;hash_key&#8221; (required) to be the only attribute that&#8217;ll need to be unique and present on each read/write - the aadObjectId, that identifies a particular user&#8217;s conversation. </p><blockquote><p>Note there&#8217;s no &#8220;contents&#8221; field created here. DyanmoDB is really cool here - it doesn&#8217;t require non-key fields to be pre-defined when creating the table. The schema is flexible. #Neat</p></blockquote><p>We do set the TTL field (optional) called deleteAt, which can be used to delete the tokens. We do populate this in our code, but we handle all cleanup via code paths directly, I&#8217;m not clear if DynamoDB can do this itself asynchronously of our application. </p><blockquote><p>If you know, please tell me! </p></blockquote><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist138741431\&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-conversation_table-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;conversation_table.tf content, created by KyMidd on 10: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;conversation_table.tf\&quot;>\n        <tr>\n          <td id=\&quot;file-conversation_table-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-conversation_table-tf-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=\&quot;pl-en\&quot;>locals</span> {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-conversation_table-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-conversation_table-tf-LC2\&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> This is the name of the DynamoDB table<span class=\&quot;pl-c\&quot;></span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-conversation_table-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-conversation_table-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;>conversation_table_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>VeraTeamsConversations<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-conversation_table-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-conversation_table-tf-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>}</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-conversation_table-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-conversation_table-tf-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-conversation_table-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-conversation_table-tf-LC6\&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_dynamodb_table&amp;quot;</span> <span class=\&quot;pl-smi\&quot;>&amp;quot;conversations&amp;quot;</span> {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-conversation_table-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-conversation_table-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;>name</span>           <span class=\&quot;pl-k\&quot;>=</span> </span>local<span class=\&quot;pl-k\&quot;>.</span><span class=\&quot;pl-smi\&quot;>conversation_table_name</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-conversation_table-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-conversation_table-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;>billing_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>PROVISIONED<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-conversation_table-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-conversation_table-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;>read_capacity</span>  <span class=\&quot;pl-k\&quot;>=</span> </span><span class=\&quot;pl-c1\&quot;>20</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-conversation_table-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-conversation_table-tf-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  <span class=\&quot;pl-v\&quot;><span class=\&quot;pl-smi\&quot;>write_capacity</span> <span class=\&quot;pl-k\&quot;>=</span> </span><span class=\&quot;pl-c1\&quot;>20</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-conversation_table-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-conversation_table-tf-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  <span class=\&quot;pl-v\&quot;><span class=\&quot;pl-smi\&quot;>hash_key</span>       <span class=\&quot;pl-k\&quot;>=</span> </span><span class=\&quot;pl-s\&quot;><span class=\&quot;pl-pds\&quot;>&amp;quot;</span>aadObjectId<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-conversation_table-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-conversation_table-tf-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-conversation_table-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-conversation_table-tf-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  <span class=\&quot;pl-en\&quot;>attribute</span> {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-conversation_table-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-conversation_table-tf-LC14\&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>aadObjectId<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-conversation_table-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-conversation_table-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>S<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-conversation_table-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-conversation_table-tf-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  }</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-conversation_table-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-conversation_table-tf-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-conversation_table-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-conversation_table-tf-LC18\&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> Store entire conversation event as a JSON string with name conversationEvent<span class=\&quot;pl-c\&quot;></span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-conversation_table-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-conversation_table-tf-LC19\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-conversation_table-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-conversation_table-tf-LC20\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  <span class=\&quot;pl-en\&quot;>ttl</span> {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-conversation_table-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-conversation_table-tf-LC21\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=\&quot;pl-v\&quot;><span class=\&quot;pl-smi\&quot;>attribute_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>deleteAt<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span> <span class=\&quot;pl-c\&quot;><span class=\&quot;pl-c\&quot;>#</span> Epoch time in seconds, needs to match token validity<span class=\&quot;pl-c\&quot;></span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-conversation_table-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-conversation_table-tf-LC22\&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-conversation_table-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-conversation_table-tf-LC23\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  }</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-conversation_table-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-conversation_table-tf-LC24\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-conversation_table-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-conversation_table-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;>tags</span> <span class=\&quot;pl-k\&quot;>=</span> </span>{</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-conversation_table-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-conversation_table-tf-LC26\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    Name <span class=\&quot;pl-k\&quot;>=</span> local.conversation_table_name</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-conversation_table-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-conversation_table-tf-LC27\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  }</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-conversation_table-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-conversation_table-tf-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/6fcd8ca75fc0218886a9d43cd7892091/raw/8cc5e1f62fe76c84dc5b5ecabd3b24c3aded2dc5/conversation_table.tf\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/6fcd8ca75fc0218886a9d43cd7892091#file-conversation_table-tf\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          conversation_table.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-05ac2616078a.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-05ac2616078a.css"><div id="gist138741431" 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-conversation_table-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="8" data-paste-markdown-skip="" data-tagsearch-path="conversation_table.tf">
        <tbody><tr>
          <td id="file-conversation_table-tf-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-conversation_table-tf-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-en">locals</span> {</td>
        </tr>
        <tr>
          <td id="file-conversation_table-tf-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-conversation_table-tf-LC2" class="blob-code blob-code-inner js-file-line">  <span class="pl-c"><span class="pl-c">#</span> This is the name of the DynamoDB table<span class="pl-c"></span></span></td>
        </tr>
        <tr>
          <td id="file-conversation_table-tf-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-conversation_table-tf-LC3" class="blob-code blob-code-inner js-file-line">  <span class="pl-v"><span class="pl-smi">conversation_table_name</span> <span class="pl-k">=</span> </span><span class="pl-s"><span class="pl-pds">"</span>VeraTeamsConversations<span class="pl-pds">"</span></span></td>
        </tr>
        <tr>
          <td id="file-conversation_table-tf-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-conversation_table-tf-LC4" class="blob-code blob-code-inner js-file-line">}</td>
        </tr>
        <tr>
          <td id="file-conversation_table-tf-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-conversation_table-tf-LC5" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-conversation_table-tf-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-conversation_table-tf-LC6" class="blob-code blob-code-inner js-file-line"><span class="pl-en">resource</span> <span class="pl-smi">"aws_dynamodb_table"</span> <span class="pl-smi">"conversations"</span> {</td>
        </tr>
        <tr>
          <td id="file-conversation_table-tf-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-conversation_table-tf-LC7" 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>local<span class="pl-k">.</span><span class="pl-smi">conversation_table_name</span></td>
        </tr>
        <tr>
          <td id="file-conversation_table-tf-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-conversation_table-tf-LC8" class="blob-code blob-code-inner js-file-line">  <span class="pl-v"><span class="pl-smi">billing_mode</span>   <span class="pl-k">=</span> </span><span class="pl-s"><span class="pl-pds">"</span>PROVISIONED<span class="pl-pds">"</span></span></td>
        </tr>
        <tr>
          <td id="file-conversation_table-tf-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-conversation_table-tf-LC9" class="blob-code blob-code-inner js-file-line">  <span class="pl-v"><span class="pl-smi">read_capacity</span>  <span class="pl-k">=</span> </span><span class="pl-c1">20</span></td>
        </tr>
        <tr>
          <td id="file-conversation_table-tf-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-conversation_table-tf-LC10" class="blob-code blob-code-inner js-file-line">  <span class="pl-v"><span class="pl-smi">write_capacity</span> <span class="pl-k">=</span> </span><span class="pl-c1">20</span></td>
        </tr>
        <tr>
          <td id="file-conversation_table-tf-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-conversation_table-tf-LC11" class="blob-code blob-code-inner js-file-line">  <span class="pl-v"><span class="pl-smi">hash_key</span>       <span class="pl-k">=</span> </span><span class="pl-s"><span class="pl-pds">"</span>aadObjectId<span class="pl-pds">"</span></span></td>
        </tr>
        <tr>
          <td id="file-conversation_table-tf-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-conversation_table-tf-LC12" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-conversation_table-tf-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-conversation_table-tf-LC13" class="blob-code blob-code-inner js-file-line">  <span class="pl-en">attribute</span> {</td>
        </tr>
        <tr>
          <td id="file-conversation_table-tf-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-conversation_table-tf-LC14" 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>aadObjectId<span class="pl-pds">"</span></span></td>
        </tr>
        <tr>
          <td id="file-conversation_table-tf-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-conversation_table-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>S<span class="pl-pds">"</span></span></td>
        </tr>
        <tr>
          <td id="file-conversation_table-tf-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-conversation_table-tf-LC16" class="blob-code blob-code-inner js-file-line">  }</td>
        </tr>
        <tr>
          <td id="file-conversation_table-tf-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-conversation_table-tf-LC17" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-conversation_table-tf-L18" class="blob-num js-line-number js-blob-rnum" data-line-number="18"></td>
          <td id="file-conversation_table-tf-LC18" class="blob-code blob-code-inner js-file-line">  <span class="pl-c"><span class="pl-c">#</span> Store entire conversation event as a JSON string with name conversationEvent<span class="pl-c"></span></span></td>
        </tr>
        <tr>
          <td id="file-conversation_table-tf-L19" class="blob-num js-line-number js-blob-rnum" data-line-number="19"></td>
          <td id="file-conversation_table-tf-LC19" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-conversation_table-tf-L20" class="blob-num js-line-number js-blob-rnum" data-line-number="20"></td>
          <td id="file-conversation_table-tf-LC20" class="blob-code blob-code-inner js-file-line">  <span class="pl-en">ttl</span> {</td>
        </tr>
        <tr>
          <td id="file-conversation_table-tf-L21" class="blob-num js-line-number js-blob-rnum" data-line-number="21"></td>
          <td id="file-conversation_table-tf-LC21" class="blob-code blob-code-inner js-file-line">    <span class="pl-v"><span class="pl-smi">attribute_name</span> <span class="pl-k">=</span> </span><span class="pl-s"><span class="pl-pds">"</span>deleteAt<span class="pl-pds">"</span></span> <span class="pl-c"><span class="pl-c">#</span> Epoch time in seconds, needs to match token validity<span class="pl-c"></span></span></td>
        </tr>
        <tr>
          <td id="file-conversation_table-tf-L22" class="blob-num js-line-number js-blob-rnum" data-line-number="22"></td>
          <td id="file-conversation_table-tf-LC22" 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-conversation_table-tf-L23" class="blob-num js-line-number js-blob-rnum" data-line-number="23"></td>
          <td id="file-conversation_table-tf-LC23" class="blob-code blob-code-inner js-file-line">  }</td>
        </tr>
        <tr>
          <td id="file-conversation_table-tf-L24" class="blob-num js-line-number js-blob-rnum" data-line-number="24"></td>
          <td id="file-conversation_table-tf-LC24" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-conversation_table-tf-L25" class="blob-num js-line-number js-blob-rnum" data-line-number="25"></td>
          <td id="file-conversation_table-tf-LC25" class="blob-code blob-code-inner js-file-line">  <span class="pl-v"><span class="pl-smi">tags</span> <span class="pl-k">=</span> </span>{</td>
        </tr>
        <tr>
          <td id="file-conversation_table-tf-L26" class="blob-num js-line-number js-blob-rnum" data-line-number="26"></td>
          <td id="file-conversation_table-tf-LC26" class="blob-code blob-code-inner js-file-line">    Name <span class="pl-k">=</span> local.conversation_table_name</td>
        </tr>
        <tr>
          <td id="file-conversation_table-tf-L27" class="blob-num js-line-number js-blob-rnum" data-line-number="27"></td>
          <td id="file-conversation_table-tf-LC27" class="blob-code blob-code-inner js-file-line">  }</td>
        </tr>
        <tr>
          <td id="file-conversation_table-tf-L28" class="blob-num js-line-number js-blob-rnum" data-line-number="28"></td>
          <td id="file-conversation_table-tf-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/6fcd8ca75fc0218886a9d43cd7892091/raw/8cc5e1f62fe76c84dc5b5ecabd3b24c3aded2dc5/conversation_table.tf" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/6fcd8ca75fc0218886a9d43cd7892091#file-conversation_table-tf" class="Link--inTextBlock">
          conversation_table.tf
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>Next, the Token table. It looks almost exactly the same. We also use the hash_key here of the aadObjectId, since both the webhook and the Token come in with the user&#8217;s ID on it, and it works great for identifying ownership of these items. </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist138741482\&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-token_table-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;token_table.tf content, created by KyMidd on 10: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;token_table.tf\&quot;>\n        <tr>\n          <td id=\&quot;file-token_table-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-token_table-tf-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=\&quot;pl-en\&quot;>locals</span> {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-token_table-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-token_table-tf-LC2\&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> This is the name of the DynamoDB table<span class=\&quot;pl-c\&quot;></span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-token_table-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-token_table-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;>token_table_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>VeraTeamsTokens<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-token_table-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-token_table-tf-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>}</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-token_table-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-token_table-tf-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-token_table-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-token_table-tf-LC6\&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_dynamodb_table&amp;quot;</span> <span class=\&quot;pl-smi\&quot;>&amp;quot;tokens&amp;quot;</span> {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-token_table-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-token_table-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;>name</span>           <span class=\&quot;pl-k\&quot;>=</span> </span>local<span class=\&quot;pl-k\&quot;>.</span><span class=\&quot;pl-smi\&quot;>token_table_name</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-token_table-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-token_table-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;>billing_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>PROVISIONED<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-token_table-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-token_table-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;>read_capacity</span>  <span class=\&quot;pl-k\&quot;>=</span> </span><span class=\&quot;pl-c1\&quot;>20</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-token_table-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-token_table-tf-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  <span class=\&quot;pl-v\&quot;><span class=\&quot;pl-smi\&quot;>write_capacity</span> <span class=\&quot;pl-k\&quot;>=</span> </span><span class=\&quot;pl-c1\&quot;>20</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-token_table-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-token_table-tf-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  <span class=\&quot;pl-v\&quot;><span class=\&quot;pl-smi\&quot;>hash_key</span>       <span class=\&quot;pl-k\&quot;>=</span> </span><span class=\&quot;pl-s\&quot;><span class=\&quot;pl-pds\&quot;>&amp;quot;</span>aadObjectId<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-token_table-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-token_table-tf-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-token_table-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-token_table-tf-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  <span class=\&quot;pl-en\&quot;>attribute</span> {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-token_table-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-token_table-tf-LC14\&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>aadObjectId<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-token_table-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-token_table-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>S<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-token_table-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-token_table-tf-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  }</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-token_table-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-token_table-tf-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-token_table-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-token_table-tf-LC18\&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> Store accessToken from oauth2 server as string with name accessToken<span class=\&quot;pl-c\&quot;></span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-token_table-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-token_table-tf-LC19\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-token_table-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-token_table-tf-LC20\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  <span class=\&quot;pl-en\&quot;>ttl</span> {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-token_table-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-token_table-tf-LC21\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=\&quot;pl-v\&quot;><span class=\&quot;pl-smi\&quot;>attribute_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>deleteAt<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span> <span class=\&quot;pl-c\&quot;><span class=\&quot;pl-c\&quot;>#</span> Epoch time in seconds, needs to match token validity<span class=\&quot;pl-c\&quot;></span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-token_table-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-token_table-tf-LC22\&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-token_table-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-token_table-tf-LC23\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  }</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-token_table-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-token_table-tf-LC24\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-token_table-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-token_table-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;>tags</span> <span class=\&quot;pl-k\&quot;>=</span> </span>{</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-token_table-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-token_table-tf-LC26\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    Name <span class=\&quot;pl-k\&quot;>=</span> local.token_table_name</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-token_table-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-token_table-tf-LC27\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  }</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-token_table-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-token_table-tf-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/082d414b7e98415b6b4cb489c7692d09/raw/39e3d86fb4898e4cc2a6080bbaf755523de103da/token_table.tf\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/082d414b7e98415b6b4cb489c7692d09#file-token_table-tf\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          token_table.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-05ac2616078a.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-05ac2616078a.css"><div id="gist138741482" 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-token_table-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="8" data-paste-markdown-skip="" data-tagsearch-path="token_table.tf">
        <tbody><tr>
          <td id="file-token_table-tf-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-token_table-tf-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-en">locals</span> {</td>
        </tr>
        <tr>
          <td id="file-token_table-tf-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-token_table-tf-LC2" class="blob-code blob-code-inner js-file-line">  <span class="pl-c"><span class="pl-c">#</span> This is the name of the DynamoDB table<span class="pl-c"></span></span></td>
        </tr>
        <tr>
          <td id="file-token_table-tf-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-token_table-tf-LC3" class="blob-code blob-code-inner js-file-line">  <span class="pl-v"><span class="pl-smi">token_table_name</span> <span class="pl-k">=</span> </span><span class="pl-s"><span class="pl-pds">"</span>VeraTeamsTokens<span class="pl-pds">"</span></span></td>
        </tr>
        <tr>
          <td id="file-token_table-tf-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-token_table-tf-LC4" class="blob-code blob-code-inner js-file-line">}</td>
        </tr>
        <tr>
          <td id="file-token_table-tf-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-token_table-tf-LC5" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-token_table-tf-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-token_table-tf-LC6" class="blob-code blob-code-inner js-file-line"><span class="pl-en">resource</span> <span class="pl-smi">"aws_dynamodb_table"</span> <span class="pl-smi">"tokens"</span> {</td>
        </tr>
        <tr>
          <td id="file-token_table-tf-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-token_table-tf-LC7" 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>local<span class="pl-k">.</span><span class="pl-smi">token_table_name</span></td>
        </tr>
        <tr>
          <td id="file-token_table-tf-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-token_table-tf-LC8" class="blob-code blob-code-inner js-file-line">  <span class="pl-v"><span class="pl-smi">billing_mode</span>   <span class="pl-k">=</span> </span><span class="pl-s"><span class="pl-pds">"</span>PROVISIONED<span class="pl-pds">"</span></span></td>
        </tr>
        <tr>
          <td id="file-token_table-tf-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-token_table-tf-LC9" class="blob-code blob-code-inner js-file-line">  <span class="pl-v"><span class="pl-smi">read_capacity</span>  <span class="pl-k">=</span> </span><span class="pl-c1">20</span></td>
        </tr>
        <tr>
          <td id="file-token_table-tf-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-token_table-tf-LC10" class="blob-code blob-code-inner js-file-line">  <span class="pl-v"><span class="pl-smi">write_capacity</span> <span class="pl-k">=</span> </span><span class="pl-c1">20</span></td>
        </tr>
        <tr>
          <td id="file-token_table-tf-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-token_table-tf-LC11" class="blob-code blob-code-inner js-file-line">  <span class="pl-v"><span class="pl-smi">hash_key</span>       <span class="pl-k">=</span> </span><span class="pl-s"><span class="pl-pds">"</span>aadObjectId<span class="pl-pds">"</span></span></td>
        </tr>
        <tr>
          <td id="file-token_table-tf-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-token_table-tf-LC12" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-token_table-tf-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-token_table-tf-LC13" class="blob-code blob-code-inner js-file-line">  <span class="pl-en">attribute</span> {</td>
        </tr>
        <tr>
          <td id="file-token_table-tf-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-token_table-tf-LC14" 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>aadObjectId<span class="pl-pds">"</span></span></td>
        </tr>
        <tr>
          <td id="file-token_table-tf-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-token_table-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>S<span class="pl-pds">"</span></span></td>
        </tr>
        <tr>
          <td id="file-token_table-tf-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-token_table-tf-LC16" class="blob-code blob-code-inner js-file-line">  }</td>
        </tr>
        <tr>
          <td id="file-token_table-tf-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-token_table-tf-LC17" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-token_table-tf-L18" class="blob-num js-line-number js-blob-rnum" data-line-number="18"></td>
          <td id="file-token_table-tf-LC18" class="blob-code blob-code-inner js-file-line">  <span class="pl-c"><span class="pl-c">#</span> Store accessToken from oauth2 server as string with name accessToken<span class="pl-c"></span></span></td>
        </tr>
        <tr>
          <td id="file-token_table-tf-L19" class="blob-num js-line-number js-blob-rnum" data-line-number="19"></td>
          <td id="file-token_table-tf-LC19" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-token_table-tf-L20" class="blob-num js-line-number js-blob-rnum" data-line-number="20"></td>
          <td id="file-token_table-tf-LC20" class="blob-code blob-code-inner js-file-line">  <span class="pl-en">ttl</span> {</td>
        </tr>
        <tr>
          <td id="file-token_table-tf-L21" class="blob-num js-line-number js-blob-rnum" data-line-number="21"></td>
          <td id="file-token_table-tf-LC21" class="blob-code blob-code-inner js-file-line">    <span class="pl-v"><span class="pl-smi">attribute_name</span> <span class="pl-k">=</span> </span><span class="pl-s"><span class="pl-pds">"</span>deleteAt<span class="pl-pds">"</span></span> <span class="pl-c"><span class="pl-c">#</span> Epoch time in seconds, needs to match token validity<span class="pl-c"></span></span></td>
        </tr>
        <tr>
          <td id="file-token_table-tf-L22" class="blob-num js-line-number js-blob-rnum" data-line-number="22"></td>
          <td id="file-token_table-tf-LC22" 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-token_table-tf-L23" class="blob-num js-line-number js-blob-rnum" data-line-number="23"></td>
          <td id="file-token_table-tf-LC23" class="blob-code blob-code-inner js-file-line">  }</td>
        </tr>
        <tr>
          <td id="file-token_table-tf-L24" class="blob-num js-line-number js-blob-rnum" data-line-number="24"></td>
          <td id="file-token_table-tf-LC24" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-token_table-tf-L25" class="blob-num js-line-number js-blob-rnum" data-line-number="25"></td>
          <td id="file-token_table-tf-LC25" class="blob-code blob-code-inner js-file-line">  <span class="pl-v"><span class="pl-smi">tags</span> <span class="pl-k">=</span> </span>{</td>
        </tr>
        <tr>
          <td id="file-token_table-tf-L26" class="blob-num js-line-number js-blob-rnum" data-line-number="26"></td>
          <td id="file-token_table-tf-LC26" class="blob-code blob-code-inner js-file-line">    Name <span class="pl-k">=</span> local.token_table_name</td>
        </tr>
        <tr>
          <td id="file-token_table-tf-L27" class="blob-num js-line-number js-blob-rnum" data-line-number="27"></td>
          <td id="file-token_table-tf-LC27" class="blob-code blob-code-inner js-file-line">  }</td>
        </tr>
        <tr>
          <td id="file-token_table-tf-L28" class="blob-num js-line-number js-blob-rnum" data-line-number="28"></td>
          <td id="file-token_table-tf-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/082d414b7e98415b6b4cb489c7692d09/raw/39e3d86fb4898e4cc2a6080bbaf755523de103da/token_table.tf" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/082d414b7e98415b6b4cb489c7692d09#file-token_table-tf" class="Link--inTextBlock">
          token_table.tf
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>Next, we create the KMS CMK. I specify that this key should automatically rotate keying materials, a good security practice. I don&#8217;t think this is handled in my application - we should likely not rotate key materials. The worst case scenario here is a key is stored, and then the keying material changes, and the key can&#8217;t be decrypted, and the app would malfunction for that user for the hour-ish that their key is valid. Once it expired, it&#8217;d be regenerated, restored, and the app would work for them again. </p><blockquote><p>If you&#8217;re building this at home, turn off key rotation</p></blockquote><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist138741489\&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-cmk-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;cmk.tf content, created by KyMidd on 10:15PM 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;cmk.tf\&quot;>\n        <tr>\n          <td id=\&quot;file-cmk-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-cmk-tf-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=\&quot;pl-en\&quot;>locals</span> {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-cmk-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-cmk-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;>cmk_alias</span> <span class=\&quot;pl-k\&quot;>=</span> </span><span class=\&quot;pl-s\&quot;><span class=\&quot;pl-pds\&quot;>&amp;quot;</span>alias/VeraCmk<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-cmk-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-cmk-tf-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>}</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-cmk-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-cmk-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-cmk-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-cmk-tf-LC5\&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> Create the KMS key<span class=\&quot;pl-c\&quot;></span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-cmk-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-cmk-tf-LC6\&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_kms_key&amp;quot;</span> <span class=\&quot;pl-smi\&quot;>&amp;quot;cmk&amp;quot;</span> {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-cmk-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-cmk-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;>description</span>             <span class=\&quot;pl-k\&quot;>=</span> </span><span class=\&quot;pl-s\&quot;><span class=\&quot;pl-pds\&quot;>&amp;quot;</span>Key for encrypting access tokens between Vera lambda layers<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-cmk-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-cmk-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;>deletion_window_in_days</span> <span class=\&quot;pl-k\&quot;>=</span> </span><span class=\&quot;pl-c1\&quot;>10</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-cmk-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-cmk-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;>enable_key_rotation</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-cmk-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-cmk-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-cmk-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-cmk-tf-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  <span class=\&quot;pl-v\&quot;><span class=\&quot;pl-smi\&quot;>tags</span> <span class=\&quot;pl-k\&quot;>=</span> </span>{</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-cmk-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-cmk-tf-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    Name <span class=\&quot;pl-k\&quot;>=</span> <span class=\&quot;pl-s\&quot;><span class=\&quot;pl-pds\&quot;>&amp;quot;</span>VeraCmk<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-cmk-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-cmk-tf-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  }</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-cmk-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-cmk-tf-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>}</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-cmk-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-cmk-tf-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>\n</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-cmk-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-cmk-tf-LC16\&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> Optional alias for easier reference<span class=\&quot;pl-c\&quot;></span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-cmk-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-cmk-tf-LC17\&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_kms_alias&amp;quot;</span> <span class=\&quot;pl-smi\&quot;>&amp;quot;cmk_alias&amp;quot;</span> {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-cmk-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-cmk-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;>name</span>          <span class=\&quot;pl-k\&quot;>=</span> </span>local<span class=\&quot;pl-k\&quot;>.</span><span class=\&quot;pl-smi\&quot;>cmk_alias</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-cmk-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-cmk-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;>target_key_id</span> <span class=\&quot;pl-k\&quot;>=</span> </span>aws_kms_key<span class=\&quot;pl-k\&quot;>.</span><span class=\&quot;pl-smi\&quot;>cmk</span><span class=\&quot;pl-k\&quot;>.</span><span class=\&quot;pl-smi\&quot;>key_id</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-cmk-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-cmk-tf-LC20\&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/c19f7db81a3c7a3a032784f6b1621da7/raw/4a10c598833c0e7c7bf91fb4b42764ba45805479/cmk.tf\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/c19f7db81a3c7a3a032784f6b1621da7#file-cmk-tf\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          cmk.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-05ac2616078a.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-05ac2616078a.css"><div id="gist138741489" 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-cmk-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="8" data-paste-markdown-skip="" data-tagsearch-path="cmk.tf">
        <tbody><tr>
          <td id="file-cmk-tf-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-cmk-tf-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-en">locals</span> {</td>
        </tr>
        <tr>
          <td id="file-cmk-tf-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-cmk-tf-LC2" class="blob-code blob-code-inner js-file-line">  <span class="pl-v"><span class="pl-smi">cmk_alias</span> <span class="pl-k">=</span> </span><span class="pl-s"><span class="pl-pds">"</span>alias/VeraCmk<span class="pl-pds">"</span></span></td>
        </tr>
        <tr>
          <td id="file-cmk-tf-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-cmk-tf-LC3" class="blob-code blob-code-inner js-file-line">}</td>
        </tr>
        <tr>
          <td id="file-cmk-tf-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-cmk-tf-LC4" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-cmk-tf-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-cmk-tf-LC5" class="blob-code blob-code-inner js-file-line"><span class="pl-c"><span class="pl-c">#</span> Create the KMS key<span class="pl-c"></span></span></td>
        </tr>
        <tr>
          <td id="file-cmk-tf-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-cmk-tf-LC6" class="blob-code blob-code-inner js-file-line"><span class="pl-en">resource</span> <span class="pl-smi">"aws_kms_key"</span> <span class="pl-smi">"cmk"</span> {</td>
        </tr>
        <tr>
          <td id="file-cmk-tf-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-cmk-tf-LC7" class="blob-code blob-code-inner js-file-line">  <span class="pl-v"><span class="pl-smi">description</span>             <span class="pl-k">=</span> </span><span class="pl-s"><span class="pl-pds">"</span>Key for encrypting access tokens between Vera lambda layers<span class="pl-pds">"</span></span></td>
        </tr>
        <tr>
          <td id="file-cmk-tf-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-cmk-tf-LC8" class="blob-code blob-code-inner js-file-line">  <span class="pl-v"><span class="pl-smi">deletion_window_in_days</span> <span class="pl-k">=</span> </span><span class="pl-c1">10</span></td>
        </tr>
        <tr>
          <td id="file-cmk-tf-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-cmk-tf-LC9" class="blob-code blob-code-inner js-file-line">  <span class="pl-v"><span class="pl-smi">enable_key_rotation</span>     <span class="pl-k">=</span> </span><span class="pl-c1">true</span></td>
        </tr>
        <tr>
          <td id="file-cmk-tf-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-cmk-tf-LC10" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-cmk-tf-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-cmk-tf-LC11" class="blob-code blob-code-inner js-file-line">  <span class="pl-v"><span class="pl-smi">tags</span> <span class="pl-k">=</span> </span>{</td>
        </tr>
        <tr>
          <td id="file-cmk-tf-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-cmk-tf-LC12" class="blob-code blob-code-inner js-file-line">    Name <span class="pl-k">=</span> <span class="pl-s"><span class="pl-pds">"</span>VeraCmk<span class="pl-pds">"</span></span></td>
        </tr>
        <tr>
          <td id="file-cmk-tf-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-cmk-tf-LC13" class="blob-code blob-code-inner js-file-line">  }</td>
        </tr>
        <tr>
          <td id="file-cmk-tf-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-cmk-tf-LC14" class="blob-code blob-code-inner js-file-line">}</td>
        </tr>
        <tr>
          <td id="file-cmk-tf-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-cmk-tf-LC15" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-cmk-tf-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-cmk-tf-LC16" class="blob-code blob-code-inner js-file-line"><span class="pl-c"><span class="pl-c">#</span> Optional alias for easier reference<span class="pl-c"></span></span></td>
        </tr>
        <tr>
          <td id="file-cmk-tf-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-cmk-tf-LC17" class="blob-code blob-code-inner js-file-line"><span class="pl-en">resource</span> <span class="pl-smi">"aws_kms_alias"</span> <span class="pl-smi">"cmk_alias"</span> {</td>
        </tr>
        <tr>
          <td id="file-cmk-tf-L18" class="blob-num js-line-number js-blob-rnum" data-line-number="18"></td>
          <td id="file-cmk-tf-LC18" 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>local<span class="pl-k">.</span><span class="pl-smi">cmk_alias</span></td>
        </tr>
        <tr>
          <td id="file-cmk-tf-L19" class="blob-num js-line-number js-blob-rnum" data-line-number="19"></td>
          <td id="file-cmk-tf-LC19" class="blob-code blob-code-inner js-file-line">  <span class="pl-v"><span class="pl-smi">target_key_id</span> <span class="pl-k">=</span> </span>aws_kms_key<span class="pl-k">.</span><span class="pl-smi">cmk</span><span class="pl-k">.</span><span class="pl-smi">key_id</span></td>
        </tr>
        <tr>
          <td id="file-cmk-tf-L20" class="blob-num js-line-number js-blob-rnum" data-line-number="20"></td>
          <td id="file-cmk-tf-LC20" 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/c19f7db81a3c7a3a032784f6b1621da7/raw/4a10c598833c0e7c7bf91fb4b42764ba45805479/cmk.tf" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/c19f7db81a3c7a3a032784f6b1621da7#file-cmk-tf" class="Link--inTextBlock">
          cmk.tf
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><h1>Summary</h1><p>I had intended to cover the entirety of the strategy, resources + implementation, and application code that utilizes it, but we&#8217;re already too long, and that would be huge, so lets call it here. </p><p>This article showed why we&#8217;re pushing so hard to use Delegated Access permissions, and how Azure implements Delegated Tokens. We talked about why that method is inherently incompatible with stateless applications, and covered how we&#8217;re going to establish stateful behavior for our stateless lambda application. </p><p>Then we walked through the resources we&#8217;ll create to make that behavior work, as well as the terraform configurations to build those resources. </p><p>Next up, we&#8217;ll talk about the application changes we&#8217;ll implement to make the Vera Teams app support state, Delegated OAuth2 tokens from the user, the routing logic we&#8217;ll use to make it all work. </p><p>Hope it&#8217;s all going great out there. Hello from AWS ReInforce in Philly, I give a talk on Tuesday, and I&#8217;m writing this from a conveniently empty chair in a side hall. </p><p>Good luck out there. <br>kyler</p>]]></content:encoded></item><item><title><![CDATA[D2DO272: The Physics of DevOps]]></title><description><![CDATA[aka, how do you measure something folks can't even agree on a definition for?]]></description><link>https://www.letsdodevops.com/p/d2do272-the-physics-of-devops</link><guid isPermaLink="false">https://www.letsdodevops.com/p/d2do272-the-physics-of-devops</guid><dc:creator><![CDATA[Kyler Middleton]]></dc:creator><pubDate>Wed, 11 Jun 2025 14:02:48 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!2Nc8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ba7d6e7-77c4-462b-8f76-eb91e5597f21_2048x2048.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1><strong>Day Two DevOps</strong></h1><p><a href="https://www.linkedin.com/in/ned-bellavance/">Ned Bellavance</a> and I on <a href="https://packetpushers.net/podcast/day-two-devops/">Day Two DevOps</a> host brilliant and effusive engineers who share their lessons learned from large, impactful projects. Come soak up all the hard lessons they learned through building big stuff (and sometimes destroying big stuff), all for the low low cost of totally free.</p><h1><strong>Summary</strong></h1><p>How do you measure developer performance and productivity? On today&#8217;s Day Two DevOps, we look at different methods with guest <a href="https://www.linkedin.com/in/lauratacho/">Laura Tacho</a>, the CTO at DX. We explore industry benchmarks such as the DORA report, SPACE, and DevEx. Laura also introduces us to Core 4, a project she&#8217;s been working on that provides a new framework for measuring developer productivity and to support decision making within an organization. We also talk about how AI is shifting cognitive load to senior developers.</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><h2><strong>AdSpot: Spacelift</strong></h2><p>Founded by the creator of geopoiesis, <a href="https://spacelift.io/">Spacelift</a> provides a collaboration and automation layer for infrastructure as a code. Without going into a technical minutiae, Spacelift focuses on openness, flexibility, and customization aiming for power users. Spacelift hopes to be appreciated by more sophisticated and security-conscious users. Spacelift works with any IaC setup and is capable of working as a self-hosted agent on infrastructure, therefore offering maximum security without sacrificing the functionality. Spacelift&#8217;s solution is built on top of well-known and well-loved open source components like Docker and Open Policy Agent, allowing full customization while maintaining sensible defaults.</p><h2><strong>Episode Links:</strong></h2><p><a href="https://cloud.google.com/devops/state-of-devops">The State of DevOps report: Are you a software leader?</a> &#8211; Google</p><p><a href="https://www.linkedin.com/pulse/2024-dora-report-abi-noda-f6ebc/">2024 DORA Report</a> &#8211; LinkedIn</p><p><a href="https://getdx.com/news/introducing-the-dx-core-4/">Introducing the DX Core 4</a></p><h1><strong>Listen Now!</strong></h1><p><a href="https://packetpushers.net/podcasts/day-two-devops/d2do272-the-physics-of-devops/">Head over to Packet Pushers to listen to the podcast</a>. It&#8217;s available on Apple Podcasts, Spotify, Overcast.FM, and pca.ST (basically anywhere that will take our podcasts).</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!2Nc8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ba7d6e7-77c4-462b-8f76-eb91e5597f21_2048x2048.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!2Nc8!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ba7d6e7-77c4-462b-8f76-eb91e5597f21_2048x2048.png 424w, https://substackcdn.com/image/fetch/$s_!2Nc8!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ba7d6e7-77c4-462b-8f76-eb91e5597f21_2048x2048.png 848w, https://substackcdn.com/image/fetch/$s_!2Nc8!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ba7d6e7-77c4-462b-8f76-eb91e5597f21_2048x2048.png 1272w, https://substackcdn.com/image/fetch/$s_!2Nc8!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ba7d6e7-77c4-462b-8f76-eb91e5597f21_2048x2048.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!2Nc8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ba7d6e7-77c4-462b-8f76-eb91e5597f21_2048x2048.png" width="1456" height="1456" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7ba7d6e7-77c4-462b-8f76-eb91e5597f21_2048x2048.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1456,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:772142,&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/163866267?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ba7d6e7-77c4-462b-8f76-eb91e5597f21_2048x2048.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_!2Nc8!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ba7d6e7-77c4-462b-8f76-eb91e5597f21_2048x2048.png 424w, https://substackcdn.com/image/fetch/$s_!2Nc8!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ba7d6e7-77c4-462b-8f76-eb91e5597f21_2048x2048.png 848w, https://substackcdn.com/image/fetch/$s_!2Nc8!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ba7d6e7-77c4-462b-8f76-eb91e5597f21_2048x2048.png 1272w, https://substackcdn.com/image/fetch/$s_!2Nc8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ba7d6e7-77c4-462b-8f76-eb91e5597f21_2048x2048.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>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[🔥Building a Teams Bot with AI Capabilities - Part 2 - Register and Testing in Azure w/ Teams Developer Portal🔥]]></title><description><![CDATA[aka, json juggling, manifest mangling, error exasperation]]></description><link>https://www.letsdodevops.com/p/teamsai-2</link><guid isPermaLink="false">https://www.letsdodevops.com/p/teamsai-2</guid><dc:creator><![CDATA[Kyler Middleton]]></dc:creator><pubDate>Tue, 03 Jun 2025 14:02:49 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!LJ2U!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa37d3ce4-58b3-4d1b-8162-b267db508b7d_1872x854.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 (this article!): Register Bot in Teams with Teams Developer Portal</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: <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 to register a Teams Bot resource in Azure, and link it to an App Registration, which grants it permissions and the ability to look up information. You would think, since you&#8217;ve registered a Teams Bot resource, and linked it to the Teams &#8220;channel&#8221;, that your Bot is now present in your Teams app. You&#8217;d be wrong. </p><blockquote><p>Instead, there&#8217;s one more (surprisingly complex, poorly documented) step. </p></blockquote><p>Testing your Bot in Teams, and then submitting it to your Org for use! </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!LJ2U!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa37d3ce4-58b3-4d1b-8162-b267db508b7d_1872x854.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!LJ2U!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa37d3ce4-58b3-4d1b-8162-b267db508b7d_1872x854.png 424w, https://substackcdn.com/image/fetch/$s_!LJ2U!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa37d3ce4-58b3-4d1b-8162-b267db508b7d_1872x854.png 848w, https://substackcdn.com/image/fetch/$s_!LJ2U!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa37d3ce4-58b3-4d1b-8162-b267db508b7d_1872x854.png 1272w, https://substackcdn.com/image/fetch/$s_!LJ2U!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa37d3ce4-58b3-4d1b-8162-b267db508b7d_1872x854.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!LJ2U!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa37d3ce4-58b3-4d1b-8162-b267db508b7d_1872x854.png" width="1456" height="664" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a37d3ce4-58b3-4d1b-8162-b267db508b7d_1872x854.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:664,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:145788,&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/164891498?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa37d3ce4-58b3-4d1b-8162-b267db508b7d_1872x854.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_!LJ2U!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa37d3ce4-58b3-4d1b-8162-b267db508b7d_1872x854.png 424w, https://substackcdn.com/image/fetch/$s_!LJ2U!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa37d3ce4-58b3-4d1b-8162-b267db508b7d_1872x854.png 848w, https://substackcdn.com/image/fetch/$s_!LJ2U!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa37d3ce4-58b3-4d1b-8162-b267db508b7d_1872x854.png 1272w, https://substackcdn.com/image/fetch/$s_!LJ2U!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa37d3ce4-58b3-4d1b-8162-b267db508b7d_1872x854.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>There&#8217;s a few different ways to do this - via the Teams App, or via the Teams Developer Portal. Regardless, we&#8217;re going to need a Manifest . </p><blockquote><p>If you don&#8217;t care about reading how we built it, and would rather just read the code, it&#8217;s open-sourced here. Happy building! </p><p><a href="https://github.com/KyMidd/TeamsAIBot">https://github.com/KyMidd/TeamsAIBot</a></p></blockquote><h1>Write an App Manifest</h1><p>No matter our path forward, we need an &#8220;App Manifest&#8221;. This assigns a bunch of values we care about. Lets walk through the sections. </p><blockquote><p>We&#8217;re going to be walking through<a href="https://github.com/KyMidd/TeamsAIBot/blob/master/teams_app/vera/manifest.json"> this file</a> in this section. </p></blockquote><p>Lets walk through this line by line. </p><p>Line 2 - the schema. This is a well-defined standard from Microsoft. We&#8217;re using version 1.19. There are newer versions, but 1.19 should be supported for a good long time. </p><p>Line 3 - the version. This is our version that we control. We can publish new versions that can be published by the enterprise admin at our company as we develop the bot. Note that we can publish new code on our lambdas tons without rev&#8217;ing this, this is just for the &#8220;App&#8221; config in Teams. </p><p>Line 4 - Same manifest version</p><p>Line 5 - A GUID ID for our bot in our enterprise. It must be unique in our Org. It can&#8217;t change for new versions of the bot released in our Org. You can use <a href="https://guidgenerator.com/">any number of free tools</a> to create a GUID for your bot. </p><p>Line 7, 8 - These are the names that the Bot will be seen as in Teams, and the &#8220;full&#8221; is the hover-text. </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist138409932\&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_manifest1-json\&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-json  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;teams_manifest1.json content, created by KyMidd on 09:27PM 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_manifest1.json\&quot;>\n        <tr>\n          <td id=\&quot;file-teams_manifest1-json-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_manifest1-json-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>{</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_manifest1-json-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_manifest1-json-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  <span class=\&quot;pl-ent\&quot;>&amp;quot;$schema&amp;quot;</span>: <span class=\&quot;pl-s\&quot;><span class=\&quot;pl-pds\&quot;>&amp;quot;</span>https://developer.microsoft.com/en-us/json-schemas/teams/v1.19/MicrosoftTeams.schema.json<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_manifest1-json-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_manifest1-json-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  <span class=\&quot;pl-ent\&quot;>&amp;quot;version&amp;quot;</span>: <span class=\&quot;pl-s\&quot;><span class=\&quot;pl-pds\&quot;>&amp;quot;</span>1.0.0<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_manifest1-json-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_manifest1-json-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  <span class=\&quot;pl-ent\&quot;>&amp;quot;manifestVersion&amp;quot;</span>: <span class=\&quot;pl-s\&quot;><span class=\&quot;pl-pds\&quot;>&amp;quot;</span>1.19<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_manifest1-json-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_manifest1-json-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  <span class=\&quot;pl-ent\&quot;>&amp;quot;id&amp;quot;</span>: <span class=\&quot;pl-s\&quot;><span class=\&quot;pl-pds\&quot;>&amp;quot;</span>06a349bb-8c24-44bb-9f57-c0a0bf7bcfaa<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_manifest1-json-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_manifest1-json-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  <span class=\&quot;pl-ent\&quot;>&amp;quot;name&amp;quot;</span>: {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_manifest1-json-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_manifest1-json-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=\&quot;pl-ent\&quot;>&amp;quot;short&amp;quot;</span>: <span class=\&quot;pl-s\&quot;><span class=\&quot;pl-pds\&quot;>&amp;quot;</span>Vera<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_manifest1-json-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_manifest1-json-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=\&quot;pl-ent\&quot;>&amp;quot;full&amp;quot;</span>: <span class=\&quot;pl-s\&quot;><span class=\&quot;pl-pds\&quot;>&amp;quot;</span>Vera Bot for Teams<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_manifest1-json-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_manifest1-json-LC9\&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/0192bc077f3d113ff5e3665081410761/raw/dff5167651f499855374c988560d114934e59e4f/teams_manifest1.json\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/0192bc077f3d113ff5e3665081410761#file-teams_manifest1-json\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          teams_manifest1.json\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-05ac2616078a.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-05ac2616078a.css"><div id="gist138409932" 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_manifest1-json" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-json  " 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_manifest1.json">
        <tbody><tr>
          <td id="file-teams_manifest1-json-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-teams_manifest1-json-LC1" class="blob-code blob-code-inner js-file-line">{</td>
        </tr>
        <tr>
          <td id="file-teams_manifest1-json-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-teams_manifest1-json-LC2" class="blob-code blob-code-inner js-file-line">  <span class="pl-ent">"$schema"</span>: <span class="pl-s"><span class="pl-pds">"</span>https://developer.microsoft.com/en-us/json-schemas/teams/v1.19/MicrosoftTeams.schema.json<span class="pl-pds">"</span></span>,</td>
        </tr>
        <tr>
          <td id="file-teams_manifest1-json-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-teams_manifest1-json-LC3" class="blob-code blob-code-inner js-file-line">  <span class="pl-ent">"version"</span>: <span class="pl-s"><span class="pl-pds">"</span>1.0.0<span class="pl-pds">"</span></span>,</td>
        </tr>
        <tr>
          <td id="file-teams_manifest1-json-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-teams_manifest1-json-LC4" class="blob-code blob-code-inner js-file-line">  <span class="pl-ent">"manifestVersion"</span>: <span class="pl-s"><span class="pl-pds">"</span>1.19<span class="pl-pds">"</span></span>,</td>
        </tr>
        <tr>
          <td id="file-teams_manifest1-json-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-teams_manifest1-json-LC5" class="blob-code blob-code-inner js-file-line">  <span class="pl-ent">"id"</span>: <span class="pl-s"><span class="pl-pds">"</span>06a349bb-8c24-44bb-9f57-c0a0bf7bcfaa<span class="pl-pds">"</span></span>,</td>
        </tr>
        <tr>
          <td id="file-teams_manifest1-json-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-teams_manifest1-json-LC6" class="blob-code blob-code-inner js-file-line">  <span class="pl-ent">"name"</span>: {</td>
        </tr>
        <tr>
          <td id="file-teams_manifest1-json-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-teams_manifest1-json-LC7" class="blob-code blob-code-inner js-file-line">    <span class="pl-ent">"short"</span>: <span class="pl-s"><span class="pl-pds">"</span>Vera<span class="pl-pds">"</span></span>,</td>
        </tr>
        <tr>
          <td id="file-teams_manifest1-json-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-teams_manifest1-json-LC8" class="blob-code blob-code-inner js-file-line">    <span class="pl-ent">"full"</span>: <span class="pl-s"><span class="pl-pds">"</span>Vera Bot for Teams<span class="pl-pds">"</span></span></td>
        </tr>
        <tr>
          <td id="file-teams_manifest1-json-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-teams_manifest1-json-LC9" 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/0192bc077f3d113ff5e3665081410761/raw/dff5167651f499855374c988560d114934e59e4f/teams_manifest1.json" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/0192bc077f3d113ff5e3665081410761#file-teams_manifest1-json" class="Link--inTextBlock">
          teams_manifest1.json
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>Next up, the developer information. Line 2 is your name, and lines 3 - 5 are required (!) but don&#8217;t have to be valid. Set them to google if you want. I set them to an internal wiki page where I put information about the bot. </p><p>Line 8 and 9 are a description that&#8217;s shared with users of the bot. </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist138409984\&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_manifest2-json\&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-json  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;teams_manifest2.json content, created by KyMidd on 09:31PM 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_manifest2.json\&quot;>\n        <tr>\n          <td id=\&quot;file-teams_manifest2-json-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_manifest2-json-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  <span class=\&quot;pl-ent\&quot;>&amp;quot;developer&amp;quot;</span>: {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_manifest2-json-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_manifest2-json-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=\&quot;pl-ent\&quot;>&amp;quot;name&amp;quot;</span>: <span class=\&quot;pl-s\&quot;><span class=\&quot;pl-pds\&quot;>&amp;quot;</span>Your Name<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_manifest2-json-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_manifest2-json-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=\&quot;pl-ent\&quot;>&amp;quot;websiteUrl&amp;quot;</span>: <span class=\&quot;pl-s\&quot;><span class=\&quot;pl-pds\&quot;>&amp;quot;</span>https://your-site-url.com<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_manifest2-json-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_manifest2-json-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=\&quot;pl-ent\&quot;>&amp;quot;privacyUrl&amp;quot;</span>: <span class=\&quot;pl-s\&quot;><span class=\&quot;pl-pds\&quot;>&amp;quot;</span>https://your-site-url.com/privacy<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_manifest2-json-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_manifest2-json-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=\&quot;pl-ent\&quot;>&amp;quot;termsOfUseUrl&amp;quot;</span>: <span class=\&quot;pl-s\&quot;><span class=\&quot;pl-pds\&quot;>&amp;quot;</span>https://your-site-url.com/terms<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_manifest2-json-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_manifest2-json-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  },</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_manifest2-json-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_manifest2-json-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  <span class=\&quot;pl-ent\&quot;>&amp;quot;description&amp;quot;</span>: {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_manifest2-json-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_manifest2-json-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=\&quot;pl-ent\&quot;>&amp;quot;short&amp;quot;</span>: <span class=\&quot;pl-s\&quot;><span class=\&quot;pl-pds\&quot;>&amp;quot;</span>Vera is an AI assistant<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_manifest2-json-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_manifest2-json-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=\&quot;pl-ent\&quot;>&amp;quot;full&amp;quot;</span>: <span class=\&quot;pl-s\&quot;><span class=\&quot;pl-pds\&quot;>&amp;quot;</span>Vera is an AI assistant that helps you with your tasks. Vera has read much of our internal data, and can help you understand policies and do your work.<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_manifest2-json-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_manifest2-json-LC10\&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/ad1315b98e3841d5f10542bda55a76e5/raw/0edb0c3ee0625a4a434e811ca11cd8b6f88eeb48/teams_manifest2.json\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/ad1315b98e3841d5f10542bda55a76e5#file-teams_manifest2-json\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          teams_manifest2.json\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-05ac2616078a.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-05ac2616078a.css"><div id="gist138409984" 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_manifest2-json" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-json  " 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_manifest2.json">
        <tbody><tr>
          <td id="file-teams_manifest2-json-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-teams_manifest2-json-LC1" class="blob-code blob-code-inner js-file-line">  <span class="pl-ent">"developer"</span>: {</td>
        </tr>
        <tr>
          <td id="file-teams_manifest2-json-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-teams_manifest2-json-LC2" class="blob-code blob-code-inner js-file-line">    <span class="pl-ent">"name"</span>: <span class="pl-s"><span class="pl-pds">"</span>Your Name<span class="pl-pds">"</span></span>,</td>
        </tr>
        <tr>
          <td id="file-teams_manifest2-json-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-teams_manifest2-json-LC3" class="blob-code blob-code-inner js-file-line">    <span class="pl-ent">"websiteUrl"</span>: <span class="pl-s"><span class="pl-pds">"</span>https://your-site-url.com<span class="pl-pds">"</span></span>,</td>
        </tr>
        <tr>
          <td id="file-teams_manifest2-json-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-teams_manifest2-json-LC4" class="blob-code blob-code-inner js-file-line">    <span class="pl-ent">"privacyUrl"</span>: <span class="pl-s"><span class="pl-pds">"</span>https://your-site-url.com/privacy<span class="pl-pds">"</span></span>,</td>
        </tr>
        <tr>
          <td id="file-teams_manifest2-json-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-teams_manifest2-json-LC5" class="blob-code blob-code-inner js-file-line">    <span class="pl-ent">"termsOfUseUrl"</span>: <span class="pl-s"><span class="pl-pds">"</span>https://your-site-url.com/terms<span class="pl-pds">"</span></span></td>
        </tr>
        <tr>
          <td id="file-teams_manifest2-json-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-teams_manifest2-json-LC6" class="blob-code blob-code-inner js-file-line">  },</td>
        </tr>
        <tr>
          <td id="file-teams_manifest2-json-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-teams_manifest2-json-LC7" class="blob-code blob-code-inner js-file-line">  <span class="pl-ent">"description"</span>: {</td>
        </tr>
        <tr>
          <td id="file-teams_manifest2-json-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-teams_manifest2-json-LC8" class="blob-code blob-code-inner js-file-line">    <span class="pl-ent">"short"</span>: <span class="pl-s"><span class="pl-pds">"</span>Vera is an AI assistant<span class="pl-pds">"</span></span>,</td>
        </tr>
        <tr>
          <td id="file-teams_manifest2-json-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-teams_manifest2-json-LC9" class="blob-code blob-code-inner js-file-line">    <span class="pl-ent">"full"</span>: <span class="pl-s"><span class="pl-pds">"</span>Vera is an AI assistant that helps you with your tasks. Vera has read much of our internal data, and can help you understand policies and do your work.<span class="pl-pds">"</span></span></td>
        </tr>
        <tr>
          <td id="file-teams_manifest2-json-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-teams_manifest2-json-LC10" 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/ad1315b98e3841d5f10542bda55a76e5/raw/0edb0c3ee0625a4a434e811ca11cd8b6f88eeb48/teams_manifest2.json" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/ad1315b98e3841d5f10542bda55a76e5#file-teams_manifest2-json" class="Link--inTextBlock">
          teams_manifest2.json
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>Next up, the icons. You&#8217;d think, particularly with how easy this works in Slack, that this would be easy, but oh goodness, you&#8217;d be wrong. </p><p>Let&#8217;s talk about that in a minute. For now, this is the exact name (and capitalization) of the files in the zip we&#8217;ll create in a minute. </p><p>We need an outline file that&#8217;s very small and exclusively black and white (outline.png) which is used for the far-left Teams shortcut to trigger the App, and we&#8217;ll also need a full color (color.png) 192x192 icon that&#8217;s displayed as the App&#8217;s color tile in chats. </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist138410016\&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_manifest3-json\&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-json  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;teams_manifest3.json content, created by KyMidd on 09: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;8\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;teams_manifest3.json\&quot;>\n        <tr>\n          <td id=\&quot;file-teams_manifest3-json-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_manifest3-json-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;><span class=\&quot;pl-ent\&quot;>&amp;quot;icons&amp;quot;</span>: {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_manifest3-json-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_manifest3-json-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=\&quot;pl-ent\&quot;>&amp;quot;outline&amp;quot;</span>: <span class=\&quot;pl-s\&quot;><span class=\&quot;pl-pds\&quot;>&amp;quot;</span>outline.png<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_manifest3-json-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_manifest3-json-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=\&quot;pl-ent\&quot;>&amp;quot;color&amp;quot;</span>: <span class=\&quot;pl-s\&quot;><span class=\&quot;pl-pds\&quot;>&amp;quot;</span>color.png<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_manifest3-json-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_manifest3-json-LC4\&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/1e5de2ce94e27cdfcb456cd2a4c7744c/raw/3e4afd0ea3d171941de0e4afc949a0957f637730/teams_manifest3.json\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/1e5de2ce94e27cdfcb456cd2a4c7744c#file-teams_manifest3-json\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          teams_manifest3.json\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-05ac2616078a.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-05ac2616078a.css"><div id="gist138410016" 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_manifest3-json" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-json  " 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_manifest3.json">
        <tbody><tr>
          <td id="file-teams_manifest3-json-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-teams_manifest3-json-LC1" class="blob-code blob-code-inner js-file-line"><span class="pl-ent">"icons"</span>: {</td>
        </tr>
        <tr>
          <td id="file-teams_manifest3-json-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-teams_manifest3-json-LC2" class="blob-code blob-code-inner js-file-line">    <span class="pl-ent">"outline"</span>: <span class="pl-s"><span class="pl-pds">"</span>outline.png<span class="pl-pds">"</span></span>,</td>
        </tr>
        <tr>
          <td id="file-teams_manifest3-json-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-teams_manifest3-json-LC3" class="blob-code blob-code-inner js-file-line">    <span class="pl-ent">"color"</span>: <span class="pl-s"><span class="pl-pds">"</span>color.png<span class="pl-pds">"</span></span></td>
        </tr>
        <tr>
          <td id="file-teams_manifest3-json-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-teams_manifest3-json-LC4" 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/1e5de2ce94e27cdfcb456cd2a4c7744c/raw/3e4afd0ea3d171941de0e4afc949a0957f637730/teams_manifest3.json" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/1e5de2ce94e27cdfcb456cd2a4c7744c#file-teams_manifest3-json" class="Link--inTextBlock">
          teams_manifest3.json
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><p>Next up, the accent color on line 1. This is a hex color designation, and you can use <a href="https://imagecolorpicker.com/color-code/1b5271">more online free tools</a> to pick a color. </p><p>Next up, on line 2, the bots section. We are just going to use a single bot, but we need to specify the real Bot client ID here. </p><blockquote><p>The botId (line 5) and id (line 21) must match, and must be <strong>exactly the client ID for your App Registration</strong> in your Teams Tenant. </p></blockquote><p>Line 6 specifies the scopes we want our bot to work in - personal chat (1:1), team chat (Team with Channels/Posts), and group chat (multiple members, private chats). </p><p>The next few bool flags are pretty straightforward. Line 11 - false, it&#8217;s not just for notification (it does chatting, duh), line 12, does it do audio calling (no), line 13 does it support video (no), and line 14 does it support files, yes absolutely. </p><p>Last but not least, line 17. This validDomains attribute is marked as &#8220;optional&#8221; in the API spec, and it is when your App is in single-user mode, but it is absolutely required to publish your App in your Organization. You can use wildcards (as shown here) or specify exactly the domain that webhooks will be sent to in your Org. </p><div class="github-gist" data-attrs="{&quot;innerHTML&quot;:&quot;<div id=\&quot;gist138410057\&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_manifest4-json\&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-json  \&quot;\n      style=\&quot;overflow: auto\&quot; tabindex=\&quot;0\&quot; role=\&quot;region\&quot;\n      aria-label=\&quot;teams_manifest4.json content, created by KyMidd on 09: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;8\&quot; data-paste-markdown-skip data-tagsearch-path=\&quot;teams_manifest4.json\&quot;>\n        <tr>\n          <td id=\&quot;file-teams_manifest4-json-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_manifest4-json-LC1\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>{</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_manifest4-json-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_manifest4-json-LC2\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  <span class=\&quot;pl-ent\&quot;>&amp;quot;accentColor&amp;quot;</span>: <span class=\&quot;pl-s\&quot;><span class=\&quot;pl-pds\&quot;>&amp;quot;</span>#FFFFFF<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_manifest4-json-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_manifest4-json-LC3\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  <span class=\&quot;pl-ent\&quot;>&amp;quot;bots&amp;quot;</span>: [</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_manifest4-json-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_manifest4-json-LC4\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_manifest4-json-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_manifest4-json-LC5\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>      <span class=\&quot;pl-ent\&quot;>&amp;quot;botId&amp;quot;</span>: <span class=\&quot;pl-s\&quot;><span class=\&quot;pl-pds\&quot;>&amp;quot;</span>b082f92f-d90e-4694-bd76-112d55bc1220<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_manifest4-json-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_manifest4-json-LC6\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>      <span class=\&quot;pl-ent\&quot;>&amp;quot;scopes&amp;quot;</span>: [</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_manifest4-json-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_manifest4-json-LC7\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=\&quot;pl-s\&quot;><span class=\&quot;pl-pds\&quot;>&amp;quot;</span>personal<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_manifest4-json-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_manifest4-json-LC8\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=\&quot;pl-s\&quot;><span class=\&quot;pl-pds\&quot;>&amp;quot;</span>team<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_manifest4-json-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_manifest4-json-LC9\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>        <span class=\&quot;pl-s\&quot;><span class=\&quot;pl-pds\&quot;>&amp;quot;</span>groupChat<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_manifest4-json-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_manifest4-json-LC10\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>      ],</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_manifest4-json-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_manifest4-json-LC11\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>      <span class=\&quot;pl-ent\&quot;>&amp;quot;isNotificationOnly&amp;quot;</span>: <span class=\&quot;pl-c1\&quot;>false</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_manifest4-json-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_manifest4-json-LC12\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>      <span class=\&quot;pl-ent\&quot;>&amp;quot;supportsCalling&amp;quot;</span>: <span class=\&quot;pl-c1\&quot;>false</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_manifest4-json-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_manifest4-json-LC13\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>      <span class=\&quot;pl-ent\&quot;>&amp;quot;supportsVideo&amp;quot;</span>: <span class=\&quot;pl-c1\&quot;>false</span>,</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_manifest4-json-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_manifest4-json-LC14\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>      <span class=\&quot;pl-ent\&quot;>&amp;quot;supportsFiles&amp;quot;</span>: <span class=\&quot;pl-c1\&quot;>true</span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_manifest4-json-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_manifest4-json-LC15\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    }</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_manifest4-json-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_manifest4-json-LC16\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  ],</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_manifest4-json-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_manifest4-json-LC17\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  <span class=\&quot;pl-ent\&quot;>&amp;quot;validDomains&amp;quot;</span>: [</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_manifest4-json-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_manifest4-json-LC18\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=\&quot;pl-s\&quot;><span class=\&quot;pl-pds\&quot;>&amp;quot;</span>*.lambda-url.us-east-1.on.aws<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_manifest4-json-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_manifest4-json-LC19\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  ],</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_manifest4-json-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-teams_manifest4-json-LC20\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  <span class=\&quot;pl-ent\&quot;>&amp;quot;webApplicationInfo&amp;quot;</span>: {</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_manifest4-json-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-teams_manifest4-json-LC21\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>    <span class=\&quot;pl-ent\&quot;>&amp;quot;id&amp;quot;</span>: <span class=\&quot;pl-s\&quot;><span class=\&quot;pl-pds\&quot;>&amp;quot;</span>b082f92f-d90e-4694-bd76-112d55bc1220<span class=\&quot;pl-pds\&quot;>&amp;quot;</span></span></td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_manifest4-json-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-teams_manifest4-json-LC22\&quot; class=\&quot;blob-code blob-code-inner js-file-line\&quot;>  }</td>\n        </tr>\n        <tr>\n          <td id=\&quot;file-teams_manifest4-json-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-teams_manifest4-json-LC23\&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/6cd512aec78951430ba3ffba18398e24/raw/aeabe91ca95ae326709c6da84d82df416dfb5845/teams_manifest4.json\&quot; style=\&quot;float:right\&quot; class=\&quot;Link--inTextBlock\&quot;>view raw</a>\n        <a href=\&quot;https://gist.github.com/KyMidd/6cd512aec78951430ba3ffba18398e24#file-teams_manifest4-json\&quot; class=\&quot;Link--inTextBlock\&quot;>\n          teams_manifest4.json\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-05ac2616078a.css&quot;}" data-component-name="GitgistToDOM"><link rel="stylesheet" href="https://github.githubassets.com/assets/gist-embed-05ac2616078a.css"><div id="gist138410057" 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_manifest4-json" class="file my-2">
    
    <div itemprop="text" class="Box-body p-0 blob-wrapper data type-json  " 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_manifest4.json">
        <tbody><tr>
          <td id="file-teams_manifest4-json-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-teams_manifest4-json-LC1" class="blob-code blob-code-inner js-file-line">{</td>
        </tr>
        <tr>
          <td id="file-teams_manifest4-json-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-teams_manifest4-json-LC2" class="blob-code blob-code-inner js-file-line">  <span class="pl-ent">"accentColor"</span>: <span class="pl-s"><span class="pl-pds">"</span>#FFFFFF<span class="pl-pds">"</span></span>,</td>
        </tr>
        <tr>
          <td id="file-teams_manifest4-json-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-teams_manifest4-json-LC3" class="blob-code blob-code-inner js-file-line">  <span class="pl-ent">"bots"</span>: [</td>
        </tr>
        <tr>
          <td id="file-teams_manifest4-json-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-teams_manifest4-json-LC4" class="blob-code blob-code-inner js-file-line">    {</td>
        </tr>
        <tr>
          <td id="file-teams_manifest4-json-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-teams_manifest4-json-LC5" class="blob-code blob-code-inner js-file-line">      <span class="pl-ent">"botId"</span>: <span class="pl-s"><span class="pl-pds">"</span>b082f92f-d90e-4694-bd76-112d55bc1220<span class="pl-pds">"</span></span>,</td>
        </tr>
        <tr>
          <td id="file-teams_manifest4-json-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-teams_manifest4-json-LC6" class="blob-code blob-code-inner js-file-line">      <span class="pl-ent">"scopes"</span>: [</td>
        </tr>
        <tr>
          <td id="file-teams_manifest4-json-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-teams_manifest4-json-LC7" class="blob-code blob-code-inner js-file-line">        <span class="pl-s"><span class="pl-pds">"</span>personal<span class="pl-pds">"</span></span>,</td>
        </tr>
        <tr>
          <td id="file-teams_manifest4-json-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-teams_manifest4-json-LC8" class="blob-code blob-code-inner js-file-line">        <span class="pl-s"><span class="pl-pds">"</span>team<span class="pl-pds">"</span></span>,</td>
        </tr>
        <tr>
          <td id="file-teams_manifest4-json-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-teams_manifest4-json-LC9" class="blob-code blob-code-inner js-file-line">        <span class="pl-s"><span class="pl-pds">"</span>groupChat<span class="pl-pds">"</span></span></td>
        </tr>
        <tr>
          <td id="file-teams_manifest4-json-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-teams_manifest4-json-LC10" class="blob-code blob-code-inner js-file-line">      ],</td>
        </tr>
        <tr>
          <td id="file-teams_manifest4-json-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-teams_manifest4-json-LC11" class="blob-code blob-code-inner js-file-line">      <span class="pl-ent">"isNotificationOnly"</span>: <span class="pl-c1">false</span>,</td>
        </tr>
        <tr>
          <td id="file-teams_manifest4-json-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-teams_manifest4-json-LC12" class="blob-code blob-code-inner js-file-line">      <span class="pl-ent">"supportsCalling"</span>: <span class="pl-c1">false</span>,</td>
        </tr>
        <tr>
          <td id="file-teams_manifest4-json-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-teams_manifest4-json-LC13" class="blob-code blob-code-inner js-file-line">      <span class="pl-ent">"supportsVideo"</span>: <span class="pl-c1">false</span>,</td>
        </tr>
        <tr>
          <td id="file-teams_manifest4-json-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-teams_manifest4-json-LC14" class="blob-code blob-code-inner js-file-line">      <span class="pl-ent">"supportsFiles"</span>: <span class="pl-c1">true</span></td>
        </tr>
        <tr>
          <td id="file-teams_manifest4-json-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-teams_manifest4-json-LC15" class="blob-code blob-code-inner js-file-line">    }</td>
        </tr>
        <tr>
          <td id="file-teams_manifest4-json-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-teams_manifest4-json-LC16" class="blob-code blob-code-inner js-file-line">  ],</td>
        </tr>
        <tr>
          <td id="file-teams_manifest4-json-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-teams_manifest4-json-LC17" class="blob-code blob-code-inner js-file-line">  <span class="pl-ent">"validDomains"</span>: [</td>
        </tr>
        <tr>
          <td id="file-teams_manifest4-json-L18" class="blob-num js-line-number js-blob-rnum" data-line-number="18"></td>
          <td id="file-teams_manifest4-json-LC18" class="blob-code blob-code-inner js-file-line">    <span class="pl-s"><span class="pl-pds">"</span>*.lambda-url.us-east-1.on.aws<span class="pl-pds">"</span></span></td>
        </tr>
        <tr>
          <td id="file-teams_manifest4-json-L19" class="blob-num js-line-number js-blob-rnum" data-line-number="19"></td>
          <td id="file-teams_manifest4-json-LC19" class="blob-code blob-code-inner js-file-line">  ],</td>
        </tr>
        <tr>
          <td id="file-teams_manifest4-json-L20" class="blob-num js-line-number js-blob-rnum" data-line-number="20"></td>
          <td id="file-teams_manifest4-json-LC20" class="blob-code blob-code-inner js-file-line">  <span class="pl-ent">"webApplicationInfo"</span>: {</td>
        </tr>
        <tr>
          <td id="file-teams_manifest4-json-L21" class="blob-num js-line-number js-blob-rnum" data-line-number="21"></td>
          <td id="file-teams_manifest4-json-LC21" class="blob-code blob-code-inner js-file-line">    <span class="pl-ent">"id"</span>: <span class="pl-s"><span class="pl-pds">"</span>b082f92f-d90e-4694-bd76-112d55bc1220<span class="pl-pds">"</span></span></td>
        </tr>
        <tr>
          <td id="file-teams_manifest4-json-L22" class="blob-num js-line-number js-blob-rnum" data-line-number="22"></td>
          <td id="file-teams_manifest4-json-LC22" class="blob-code blob-code-inner js-file-line">  }</td>
        </tr>
        <tr>
          <td id="file-teams_manifest4-json-L23" class="blob-num js-line-number js-blob-rnum" data-line-number="23"></td>
          <td id="file-teams_manifest4-json-LC23" 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/6cd512aec78951430ba3ffba18398e24/raw/aeabe91ca95ae326709c6da84d82df416dfb5845/teams_manifest4.json" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/KyMidd/6cd512aec78951430ba3ffba18398e24#file-teams_manifest4-json" class="Link--inTextBlock">
          teams_manifest4.json
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>
</div><h1>Building Outlines and Logos</h1><p>This part is straight up obnoxious. I don&#8217;t care about logos, and at points I&#8217;d have taken any default logo at all. Instead, the Teams tool requires you provide exactly formatted (size, color depth, resolution) photos. </p><p>Rather than having some type of tool that would just generate these images, they require you to go learn about color depth in order to create a bot. </p><blockquote><p>BIG SIGH</p></blockquote><p>I don&#8217;t have a good suggestion for you on this - I asked chatGPT about 12 times to build them, and eventually it worked. If you have a photoshop smart friend, that&#8217;d be even better. </p><p>Here are the images I ended up with. </p><p>Here&#8217;s the color.png file (make sure it&#8217;s named exactly that and in the same folder as the manifest.json file). This is used for the photo tile when users are interacting with the bot. </p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!T-n5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F034b7e2d-6b13-43fb-bc10-ebe07881f0be_192x192.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!T-n5!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F034b7e2d-6b13-43fb-bc10-ebe07881f0be_192x192.png 424w, https://substackcdn.com/image/fetch/$s_!T-n5!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F034b7e2d-6b13-43fb-bc10-ebe07881f0be_192x192.png 848w, https://substackcdn.com/image/fetch/$s_!T-n5!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F034b7e2d-6b13-43fb-bc10-ebe07881f0be_192x192.png 1272w, https://substackcdn.com/image/fetch/$s_!T-n5!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F034b7e2d-6b13-43fb-bc10-ebe07881f0be_192x192.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!T-n5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F034b7e2d-6b13-43fb-bc10-ebe07881f0be_192x192.png" width="192" height="192" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/034b7e2d-6b13-43fb-bc10-ebe07881f0be_192x192.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:192,&quot;width&quot;:192,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:58004,&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/164891498?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F034b7e2d-6b13-43fb-bc10-ebe07881f0be_192x192.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_!T-n5!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F034b7e2d-6b13-43fb-bc10-ebe07881f0be_192x192.png 424w, https://substackcdn.com/image/fetch/$s_!T-n5!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F034b7e2d-6b13-43fb-bc10-ebe07881f0be_192x192.png 848w, https://substackcdn.com/image/fetch/$s_!T-n5!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F034b7e2d-6b13-43fb-bc10-ebe07881f0be_192x192.png 1272w, https://substackcdn.com/image/fetch/$s_!T-n5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F034b7e2d-6b13-43fb-bc10-ebe07881f0be_192x192.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>And this teeny tiny outline.png (again, must be named exactly that and in the same folder as the manifest.json). This is used as a tiny picture when users are picking the bot from the far-left of teams desktop (where Activity, Chat, Teams, etc. are located). </p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!xtsu!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40bb39dc-d79c-4751-96ca-c8f25c187794_32x32.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!xtsu!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40bb39dc-d79c-4751-96ca-c8f25c187794_32x32.png 424w, https://substackcdn.com/image/fetch/$s_!xtsu!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40bb39dc-d79c-4751-96ca-c8f25c187794_32x32.png 848w, https://substackcdn.com/image/fetch/$s_!xtsu!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40bb39dc-d79c-4751-96ca-c8f25c187794_32x32.png 1272w, https://substackcdn.com/image/fetch/$s_!xtsu!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40bb39dc-d79c-4751-96ca-c8f25c187794_32x32.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!xtsu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40bb39dc-d79c-4751-96ca-c8f25c187794_32x32.png" width="32" height="32" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/40bb39dc-d79c-4751-96ca-c8f25c187794_32x32.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:32,&quot;width&quot;:32,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:262,&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/164891498?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40bb39dc-d79c-4751-96ca-c8f25c187794_32x32.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_!xtsu!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40bb39dc-d79c-4751-96ca-c8f25c187794_32x32.png 424w, https://substackcdn.com/image/fetch/$s_!xtsu!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40bb39dc-d79c-4751-96ca-c8f25c187794_32x32.png 848w, https://substackcdn.com/image/fetch/$s_!xtsu!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40bb39dc-d79c-4751-96ca-c8f25c187794_32x32.png 1272w, https://substackcdn.com/image/fetch/$s_!xtsu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40bb39dc-d79c-4751-96ca-c8f25c187794_32x32.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h1>Zip it Up</h1><p>You&#8217;d think there&#8217;d be a web form where you&#8217;d provide these three things. You would be wrong. You&#8217;re required to zip them up. </p><p>And I failed for half an hour because I clicked &#8220;compress&#8221; in mac on the folder these files are in. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!zsUO!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0664e169-c9e0-4a0c-889c-8e46e6e5604c_972x440.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!zsUO!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0664e169-c9e0-4a0c-889c-8e46e6e5604c_972x440.png 424w, https://substackcdn.com/image/fetch/$s_!zsUO!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0664e169-c9e0-4a0c-889c-8e46e6e5604c_972x440.png 848w, https://substackcdn.com/image/fetch/$s_!zsUO!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0664e169-c9e0-4a0c-889c-8e46e6e5604c_972x440.png 1272w, https://substackcdn.com/image/fetch/$s_!zsUO!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0664e169-c9e0-4a0c-889c-8e46e6e5604c_972x440.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!zsUO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0664e169-c9e0-4a0c-889c-8e46e6e5604c_972x440.png" width="972" height="440" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0664e169-c9e0-4a0c-889c-8e46e6e5604c_972x440.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:440,&quot;width&quot;:972,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:122472,&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/164891498?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0664e169-c9e0-4a0c-889c-8e46e6e5604c_972x440.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_!zsUO!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0664e169-c9e0-4a0c-889c-8e46e6e5604c_972x440.png 424w, https://substackcdn.com/image/fetch/$s_!zsUO!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0664e169-c9e0-4a0c-889c-8e46e6e5604c_972x440.png 848w, https://substackcdn.com/image/fetch/$s_!zsUO!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0664e169-c9e0-4a0c-889c-8e46e6e5604c_972x440.png 1272w, https://substackcdn.com/image/fetch/$s_!zsUO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0664e169-c9e0-4a0c-889c-8e46e6e5604c_972x440.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>Instead, you have to highlight the specific files, and then right click on them, to create the zip file. Otherwise the Teams App will give you non-specific and annoying errors that just tell you the files can&#8217;t be found since it won&#8217;t recursively read your zip file. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!qrkt!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4b76fdf-c154-4603-a776-e102599ce742_896x494.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!qrkt!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4b76fdf-c154-4603-a776-e102599ce742_896x494.png 424w, https://substackcdn.com/image/fetch/$s_!qrkt!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4b76fdf-c154-4603-a776-e102599ce742_896x494.png 848w, https://substackcdn.com/image/fetch/$s_!qrkt!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4b76fdf-c154-4603-a776-e102599ce742_896x494.png 1272w, https://substackcdn.com/image/fetch/$s_!qrkt!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4b76fdf-c154-4603-a776-e102599ce742_896x494.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!qrkt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4b76fdf-c154-4603-a776-e102599ce742_896x494.png" width="896" height="494" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e4b76fdf-c154-4603-a776-e102599ce742_896x494.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:494,&quot;width&quot;:896,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:198981,&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/164891498?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4b76fdf-c154-4603-a776-e102599ce742_896x494.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_!qrkt!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4b76fdf-c154-4603-a776-e102599ce742_896x494.png 424w, https://substackcdn.com/image/fetch/$s_!qrkt!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4b76fdf-c154-4603-a776-e102599ce742_896x494.png 848w, https://substackcdn.com/image/fetch/$s_!qrkt!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4b76fdf-c154-4603-a776-e102599ce742_896x494.png 1272w, https://substackcdn.com/image/fetch/$s_!qrkt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4b76fdf-c154-4603-a776-e102599ce742_896x494.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>Method 1: Upload to Teams</h1><blockquote><p>This isn&#8217;t the method I recommend, but including for completeness. Skip to the next section for what we <strong>will</strong> do. </p></blockquote><p>Start from your Teams app on your computer. Over on the far left, click on &#8220;Apps&#8221; (usually at the bottom), and then on &#8220;Manage your apps&#8221;. </p><blockquote><p>If you don&#8217;t see &#8220;Apps&#8221; with the plus, make sure you&#8217;re in an enterprise. There is a license requirement to use this tool, personal Teams accounts can&#8217;t. </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_!EHup!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f673657-2599-458b-b856-62b831ca5974_832x748.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!EHup!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f673657-2599-458b-b856-62b831ca5974_832x748.png 424w, https://substackcdn.com/image/fetch/$s_!EHup!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f673657-2599-458b-b856-62b831ca5974_832x748.png 848w, https://substackcdn.com/image/fetch/$s_!EHup!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f673657-2599-458b-b856-62b831ca5974_832x748.png 1272w, https://substackcdn.com/image/fetch/$s_!EHup!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f673657-2599-458b-b856-62b831ca5974_832x748.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!EHup!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f673657-2599-458b-b856-62b831ca5974_832x748.png" width="832" height="748" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7f673657-2599-458b-b856-62b831ca5974_832x748.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:748,&quot;width&quot;:832,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:110812,&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/164891498?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f673657-2599-458b-b856-62b831ca5974_832x748.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_!EHup!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f673657-2599-458b-b856-62b831ca5974_832x748.png 424w, https://substackcdn.com/image/fetch/$s_!EHup!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f673657-2599-458b-b856-62b831ca5974_832x748.png 848w, https://substackcdn.com/image/fetch/$s_!EHup!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f673657-2599-458b-b856-62b831ca5974_832x748.png 1272w, https://substackcdn.com/image/fetch/$s_!EHup!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f673657-2599-458b-b856-62b831ca5974_832x748.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>Next, &#8220;Upload an app&#8221;. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!IYJx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F006af98b-b7a7-4c4a-8785-eefbf5bef9d5_974x290.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!IYJx!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F006af98b-b7a7-4c4a-8785-eefbf5bef9d5_974x290.png 424w, https://substackcdn.com/image/fetch/$s_!IYJx!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F006af98b-b7a7-4c4a-8785-eefbf5bef9d5_974x290.png 848w, https://substackcdn.com/image/fetch/$s_!IYJx!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F006af98b-b7a7-4c4a-8785-eefbf5bef9d5_974x290.png 1272w, https://substackcdn.com/image/fetch/$s_!IYJx!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F006af98b-b7a7-4c4a-8785-eefbf5bef9d5_974x290.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!IYJx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F006af98b-b7a7-4c4a-8785-eefbf5bef9d5_974x290.png" width="974" height="290" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/006af98b-b7a7-4c4a-8785-eefbf5bef9d5_974x290.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:290,&quot;width&quot;:974,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:31644,&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/164891498?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F006af98b-b7a7-4c4a-8785-eefbf5bef9d5_974x290.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_!IYJx!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F006af98b-b7a7-4c4a-8785-eefbf5bef9d5_974x290.png 424w, https://substackcdn.com/image/fetch/$s_!IYJx!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F006af98b-b7a7-4c4a-8785-eefbf5bef9d5_974x290.png 848w, https://substackcdn.com/image/fetch/$s_!IYJx!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F006af98b-b7a7-4c4a-8785-eefbf5bef9d5_974x290.png 1272w, https://substackcdn.com/image/fetch/$s_!IYJx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F006af98b-b7a7-4c4a-8785-eefbf5bef9d5_974x290.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>Then, &#8220;Upload a custom app&#8221;. </p><blockquote><p>Notably, the second option is how you can submit an App to your Enterprise Admin for approval in the enterprise, but it isn&#8217;t how we&#8217;ll proceed. </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_!jACi!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65d5dd6c-8885-409f-af5b-ff3a9e4b0a22_1352x812.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!jACi!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65d5dd6c-8885-409f-af5b-ff3a9e4b0a22_1352x812.png 424w, https://substackcdn.com/image/fetch/$s_!jACi!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65d5dd6c-8885-409f-af5b-ff3a9e4b0a22_1352x812.png 848w, https://substackcdn.com/image/fetch/$s_!jACi!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65d5dd6c-8885-409f-af5b-ff3a9e4b0a22_1352x812.png 1272w, https://substackcdn.com/image/fetch/$s_!jACi!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65d5dd6c-8885-409f-af5b-ff3a9e4b0a22_1352x812.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!jACi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65d5dd6c-8885-409f-af5b-ff3a9e4b0a22_1352x812.png" width="1352" height="812" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/65d5dd6c-8885-409f-af5b-ff3a9e4b0a22_1352x812.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:812,&quot;width&quot;:1352,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:89341,&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/164891498?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65d5dd6c-8885-409f-af5b-ff3a9e4b0a22_1352x812.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_!jACi!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65d5dd6c-8885-409f-af5b-ff3a9e4b0a22_1352x812.png 424w, https://substackcdn.com/image/fetch/$s_!jACi!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65d5dd6c-8885-409f-af5b-ff3a9e4b0a22_1352x812.png 848w, https://substackcdn.com/image/fetch/$s_!jACi!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65d5dd6c-8885-409f-af5b-ff3a9e4b0a22_1352x812.png 1272w, https://substackcdn.com/image/fetch/$s_!jACi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65d5dd6c-8885-409f-af5b-ff3a9e4b0a22_1352x812.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>If the Archive file you picked is accepted, it&#8217;ll work and let you use it. If not, you&#8217;ll get an error like this: </p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!17AZ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1bfcb36-a2ba-46d4-a1d0-7e05314d656c_1384x306.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!17AZ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1bfcb36-a2ba-46d4-a1d0-7e05314d656c_1384x306.png 424w, https://substackcdn.com/image/fetch/$s_!17AZ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1bfcb36-a2ba-46d4-a1d0-7e05314d656c_1384x306.png 848w, https://substackcdn.com/image/fetch/$s_!17AZ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1bfcb36-a2ba-46d4-a1d0-7e05314d656c_1384x306.png 1272w, https://substackcdn.com/image/fetch/$s_!17AZ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1bfcb36-a2ba-46d4-a1d0-7e05314d656c_1384x306.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!17AZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1bfcb36-a2ba-46d4-a1d0-7e05314d656c_1384x306.png" width="1384" height="306" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f1bfcb36-a2ba-46d4-a1d0-7e05314d656c_1384x306.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:306,&quot;width&quot;:1384,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:31747,&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/164891498?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1bfcb36-a2ba-46d4-a1d0-7e05314d656c_1384x306.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_!17AZ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1bfcb36-a2ba-46d4-a1d0-7e05314d656c_1384x306.png 424w, https://substackcdn.com/image/fetch/$s_!17AZ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1bfcb36-a2ba-46d4-a1d0-7e05314d656c_1384x306.png 848w, https://substackcdn.com/image/fetch/$s_!17AZ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1bfcb36-a2ba-46d4-a1d0-7e05314d656c_1384x306.png 1272w, https://substackcdn.com/image/fetch/$s_!17AZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1bfcb36-a2ba-46d4-a1d0-7e05314d656c_1384x306.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>That&#8217;s not helpful at all, right? What went wrong? No errors? </p><p>Thankfully, there&#8217;s a better way - the Teams Developer Portal. Lets switch to this and we can see what&#8217;s going wrong. </p><h1>Method 2 (way better): Teams Developer Portal</h1><p>You can find the Teams App Development portal here: <a href="https://dev.teams.microsoft.com/home">https://dev.teams.microsoft.com/home</a>. </p><p>This is a portal that exposes the same functionality as we just saw in Teams, but has way (WAY) better error handling, by which it has any type of error handling whatsoever. </p><p>Let&#8217;s go to the Teams Developer Portal, and click &#8220;Import an app&#8221; and find the same zip file we just created. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!niFk!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d70ab55-10cd-43c8-9fb2-6bc3b3fa7e80_1820x974.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!niFk!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d70ab55-10cd-43c8-9fb2-6bc3b3fa7e80_1820x974.png 424w, https://substackcdn.com/image/fetch/$s_!niFk!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d70ab55-10cd-43c8-9fb2-6bc3b3fa7e80_1820x974.png 848w, https://substackcdn.com/image/fetch/$s_!niFk!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d70ab55-10cd-43c8-9fb2-6bc3b3fa7e80_1820x974.png 1272w, https://substackcdn.com/image/fetch/$s_!niFk!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d70ab55-10cd-43c8-9fb2-6bc3b3fa7e80_1820x974.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!niFk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d70ab55-10cd-43c8-9fb2-6bc3b3fa7e80_1820x974.png" width="1456" height="779" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6d70ab55-10cd-43c8-9fb2-6bc3b3fa7e80_1820x974.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:779,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:451889,&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/164891498?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d70ab55-10cd-43c8-9fb2-6bc3b3fa7e80_1820x974.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_!niFk!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d70ab55-10cd-43c8-9fb2-6bc3b3fa7e80_1820x974.png 424w, https://substackcdn.com/image/fetch/$s_!niFk!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d70ab55-10cd-43c8-9fb2-6bc3b3fa7e80_1820x974.png 848w, https://substackcdn.com/image/fetch/$s_!niFk!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d70ab55-10cd-43c8-9fb2-6bc3b3fa7e80_1820x974.png 1272w, https://substackcdn.com/image/fetch/$s_!niFk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d70ab55-10cd-43c8-9fb2-6bc3b3fa7e80_1820x974.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>Hey, an error message! That&#8217;s awesome, we can at least know what&#8217;s wrong. You can cancel and address or (what I recommend), Import anyway. </p><p>Click on Import to move forward. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ubSx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf6fe15a-74c7-4f80-a4d4-dc86db8c5e22_1210x498.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ubSx!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf6fe15a-74c7-4f80-a4d4-dc86db8c5e22_1210x498.png 424w, https://substackcdn.com/image/fetch/$s_!ubSx!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf6fe15a-74c7-4f80-a4d4-dc86db8c5e22_1210x498.png 848w, https://substackcdn.com/image/fetch/$s_!ubSx!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf6fe15a-74c7-4f80-a4d4-dc86db8c5e22_1210x498.png 1272w, https://substackcdn.com/image/fetch/$s_!ubSx!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf6fe15a-74c7-4f80-a4d4-dc86db8c5e22_1210x498.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ubSx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf6fe15a-74c7-4f80-a4d4-dc86db8c5e22_1210x498.png" width="1210" height="498" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cf6fe15a-74c7-4f80-a4d4-dc86db8c5e22_1210x498.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:498,&quot;width&quot;:1210,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:59845,&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/164891498?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf6fe15a-74c7-4f80-a4d4-dc86db8c5e22_1210x498.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_!ubSx!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf6fe15a-74c7-4f80-a4d4-dc86db8c5e22_1210x498.png 424w, https://substackcdn.com/image/fetch/$s_!ubSx!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf6fe15a-74c7-4f80-a4d4-dc86db8c5e22_1210x498.png 848w, https://substackcdn.com/image/fetch/$s_!ubSx!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf6fe15a-74c7-4f80-a4d4-dc86db8c5e22_1210x498.png 1272w, https://substackcdn.com/image/fetch/$s_!ubSx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf6fe15a-74c7-4f80-a4d4-dc86db8c5e22_1210x498.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>You&#8217;ll see your App imported now. It&#8217;s not yet published (we haven&#8217;t fixed the error yet). You can click through the panes and verify all the settings. Feel free to make any changes you need here on this page, or on any of the &#8220;Configure&#8221; pages. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!8inL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc71c0c4e-93ed-4048-ad47-8edee2ab74af_1890x898.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!8inL!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc71c0c4e-93ed-4048-ad47-8edee2ab74af_1890x898.png 424w, https://substackcdn.com/image/fetch/$s_!8inL!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc71c0c4e-93ed-4048-ad47-8edee2ab74af_1890x898.png 848w, https://substackcdn.com/image/fetch/$s_!8inL!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc71c0c4e-93ed-4048-ad47-8edee2ab74af_1890x898.png 1272w, https://substackcdn.com/image/fetch/$s_!8inL!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc71c0c4e-93ed-4048-ad47-8edee2ab74af_1890x898.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!8inL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc71c0c4e-93ed-4048-ad47-8edee2ab74af_1890x898.png" width="1456" height="692" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c71c0c4e-93ed-4048-ad47-8edee2ab74af_1890x898.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:692,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:147482,&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/164891498?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc71c0c4e-93ed-4048-ad47-8edee2ab74af_1890x898.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_!8inL!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc71c0c4e-93ed-4048-ad47-8edee2ab74af_1890x898.png 424w, https://substackcdn.com/image/fetch/$s_!8inL!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc71c0c4e-93ed-4048-ad47-8edee2ab74af_1890x898.png 848w, https://substackcdn.com/image/fetch/$s_!8inL!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc71c0c4e-93ed-4048-ad47-8edee2ab74af_1890x898.png 1272w, https://substackcdn.com/image/fetch/$s_!8inL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc71c0c4e-93ed-4048-ad47-8edee2ab74af_1890x898.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>Once you verify everything looks good, click on &#8220;App validation&#8221; &#8212;&gt; Get started. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!6wdd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d2758b4-5936-4c2d-ae39-76b86868ffc1_2784x1546.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!6wdd!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d2758b4-5936-4c2d-ae39-76b86868ffc1_2784x1546.png 424w, https://substackcdn.com/image/fetch/$s_!6wdd!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d2758b4-5936-4c2d-ae39-76b86868ffc1_2784x1546.png 848w, https://substackcdn.com/image/fetch/$s_!6wdd!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d2758b4-5936-4c2d-ae39-76b86868ffc1_2784x1546.png 1272w, https://substackcdn.com/image/fetch/$s_!6wdd!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d2758b4-5936-4c2d-ae39-76b86868ffc1_2784x1546.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!6wdd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d2758b4-5936-4c2d-ae39-76b86868ffc1_2784x1546.png" width="1456" height="809" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8d2758b4-5936-4c2d-ae39-76b86868ffc1_2784x1546.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:809,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:356583,&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/164891498?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d2758b4-5936-4c2d-ae39-76b86868ffc1_2784x1546.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_!6wdd!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d2758b4-5936-4c2d-ae39-76b86868ffc1_2784x1546.png 424w, https://substackcdn.com/image/fetch/$s_!6wdd!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d2758b4-5936-4c2d-ae39-76b86868ffc1_2784x1546.png 848w, https://substackcdn.com/image/fetch/$s_!6wdd!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d2758b4-5936-4c2d-ae39-76b86868ffc1_2784x1546.png 1272w, https://substackcdn.com/image/fetch/$s_!6wdd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d2758b4-5936-4c2d-ae39-76b86868ffc1_2784x1546.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>You&#8217;ll see that we&#8217;re unable to move forward due to an error - we need to fix the size of our color icon! </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!DpBc!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcce17e8b-09ef-43cb-90a0-ebda9b8a216d_1370x902.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!DpBc!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcce17e8b-09ef-43cb-90a0-ebda9b8a216d_1370x902.png 424w, https://substackcdn.com/image/fetch/$s_!DpBc!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcce17e8b-09ef-43cb-90a0-ebda9b8a216d_1370x902.png 848w, https://substackcdn.com/image/fetch/$s_!DpBc!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcce17e8b-09ef-43cb-90a0-ebda9b8a216d_1370x902.png 1272w, https://substackcdn.com/image/fetch/$s_!DpBc!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcce17e8b-09ef-43cb-90a0-ebda9b8a216d_1370x902.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!DpBc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcce17e8b-09ef-43cb-90a0-ebda9b8a216d_1370x902.png" width="1370" height="902" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cce17e8b-09ef-43cb-90a0-ebda9b8a216d_1370x902.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:902,&quot;width&quot;:1370,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:127035,&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/164891498?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcce17e8b-09ef-43cb-90a0-ebda9b8a216d_1370x902.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_!DpBc!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcce17e8b-09ef-43cb-90a0-ebda9b8a216d_1370x902.png 424w, https://substackcdn.com/image/fetch/$s_!DpBc!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcce17e8b-09ef-43cb-90a0-ebda9b8a216d_1370x902.png 848w, https://substackcdn.com/image/fetch/$s_!DpBc!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcce17e8b-09ef-43cb-90a0-ebda9b8a216d_1370x902.png 1272w, https://substackcdn.com/image/fetch/$s_!DpBc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcce17e8b-09ef-43cb-90a0-ebda9b8a216d_1370x902.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>I fixed that up (by clicking back to the Branding list, and uploading an appropriately sized image), and then came back to run the validation again. This time no errors are listed. </p><p>Lets check the boxes next to each Validation acknowledgment, and click on Start validation. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!jkgK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67b05e5a-2914-4daf-b3bf-db06fe7f5e90_2504x1066.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!jkgK!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67b05e5a-2914-4daf-b3bf-db06fe7f5e90_2504x1066.png 424w, https://substackcdn.com/image/fetch/$s_!jkgK!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67b05e5a-2914-4daf-b3bf-db06fe7f5e90_2504x1066.png 848w, https://substackcdn.com/image/fetch/$s_!jkgK!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67b05e5a-2914-4daf-b3bf-db06fe7f5e90_2504x1066.png 1272w, https://substackcdn.com/image/fetch/$s_!jkgK!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67b05e5a-2914-4daf-b3bf-db06fe7f5e90_2504x1066.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!jkgK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67b05e5a-2914-4daf-b3bf-db06fe7f5e90_2504x1066.png" width="1456" height="620" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/67b05e5a-2914-4daf-b3bf-db06fe7f5e90_2504x1066.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:620,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:190129,&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/164891498?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67b05e5a-2914-4daf-b3bf-db06fe7f5e90_2504x1066.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_!jkgK!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67b05e5a-2914-4daf-b3bf-db06fe7f5e90_2504x1066.png 424w, https://substackcdn.com/image/fetch/$s_!jkgK!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67b05e5a-2914-4daf-b3bf-db06fe7f5e90_2504x1066.png 848w, https://substackcdn.com/image/fetch/$s_!jkgK!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67b05e5a-2914-4daf-b3bf-db06fe7f5e90_2504x1066.png 1272w, https://substackcdn.com/image/fetch/$s_!jkgK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67b05e5a-2914-4daf-b3bf-db06fe7f5e90_2504x1066.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 validation will run for a few minutes. You can refresh the page and navigate away, and come back. Review the errors to make sure things are working as you expect. Here&#8217;s an example of one that is working, with no significant errors. </p><blockquote><p>There <strong>are</strong> some errors below, but this isn&#8217;t pass/fail - all the things I care about work. If you want to list your App in the Teams App store for other folks to install, you probably should care more to get an A++ on your test. </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_!gsU8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fadfdb201-9c2a-437f-a1d6-e54c54a495e9_2538x1514.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gsU8!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fadfdb201-9c2a-437f-a1d6-e54c54a495e9_2538x1514.png 424w, https://substackcdn.com/image/fetch/$s_!gsU8!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fadfdb201-9c2a-437f-a1d6-e54c54a495e9_2538x1514.png 848w, https://substackcdn.com/image/fetch/$s_!gsU8!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fadfdb201-9c2a-437f-a1d6-e54c54a495e9_2538x1514.png 1272w, https://substackcdn.com/image/fetch/$s_!gsU8!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fadfdb201-9c2a-437f-a1d6-e54c54a495e9_2538x1514.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!gsU8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fadfdb201-9c2a-437f-a1d6-e54c54a495e9_2538x1514.png" width="1456" height="869" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/adfdb201-9c2a-437f-a1d6-e54c54a495e9_2538x1514.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:869,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:280911,&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/164891498?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fadfdb201-9c2a-437f-a1d6-e54c54a495e9_2538x1514.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_!gsU8!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fadfdb201-9c2a-437f-a1d6-e54c54a495e9_2538x1514.png 424w, https://substackcdn.com/image/fetch/$s_!gsU8!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fadfdb201-9c2a-437f-a1d6-e54c54a495e9_2538x1514.png 848w, https://substackcdn.com/image/fetch/$s_!gsU8!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fadfdb201-9c2a-437f-a1d6-e54c54a495e9_2538x1514.png 1272w, https://substackcdn.com/image/fetch/$s_!gsU8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fadfdb201-9c2a-437f-a1d6-e54c54a495e9_2538x1514.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>Testing / Playing</h1><p>But you don&#8217;t care about some tests, you want to USE the app, right? There&#8217;s two ways to do this. </p><p>First way, you can &#8220;Preview in Teams&#8221; which will open the bot (default on the web, but you can use in your App as well) the bot in a 1:1 DM situation. You can attempt to use the bot. </p><blockquote><p>Note 1: If you do this, others won&#8217;t be able to see the bot or use it. </p><p>Note 2: If you&#8217;re following along in order, none of the code has been deployed yet, so of course it won&#8217;t work. This is an example of it already having been built. </p></blockquote><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!WBEI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc7ca9dac-d1aa-4ca8-bf8e-74d07d15273d_2312x400.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!WBEI!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc7ca9dac-d1aa-4ca8-bf8e-74d07d15273d_2312x400.png 424w, https://substackcdn.com/image/fetch/$s_!WBEI!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc7ca9dac-d1aa-4ca8-bf8e-74d07d15273d_2312x400.png 848w, https://substackcdn.com/image/fetch/$s_!WBEI!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc7ca9dac-d1aa-4ca8-bf8e-74d07d15273d_2312x400.png 1272w, https://substackcdn.com/image/fetch/$s_!WBEI!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc7ca9dac-d1aa-4ca8-bf8e-74d07d15273d_2312x400.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!WBEI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc7ca9dac-d1aa-4ca8-bf8e-74d07d15273d_2312x400.png" width="1456" height="252" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c7ca9dac-d1aa-4ca8-bf8e-74d07d15273d_2312x400.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:252,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:88687,&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/164891498?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc7ca9dac-d1aa-4ca8-bf8e-74d07d15273d_2312x400.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_!WBEI!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc7ca9dac-d1aa-4ca8-bf8e-74d07d15273d_2312x400.png 424w, https://substackcdn.com/image/fetch/$s_!WBEI!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc7ca9dac-d1aa-4ca8-bf8e-74d07d15273d_2312x400.png 848w, https://substackcdn.com/image/fetch/$s_!WBEI!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc7ca9dac-d1aa-4ca8-bf8e-74d07d15273d_2312x400.png 1272w, https://substackcdn.com/image/fetch/$s_!WBEI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc7ca9dac-d1aa-4ca8-bf8e-74d07d15273d_2312x400.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>The way to really get this going is to Publish the app to your Org. That way others can use the bot as well. </p><p>In the far bottom left, click on &#8220;Publish to Org&#8221; and then &#8220;Publish your app&#8221; purple button below. This will inform your Teams Enterprise Admins that your app is ready for publication. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!PgMu!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36f933b5-4372-4632-949b-ffeecb373939_1692x1260.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!PgMu!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36f933b5-4372-4632-949b-ffeecb373939_1692x1260.png 424w, https://substackcdn.com/image/fetch/$s_!PgMu!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36f933b5-4372-4632-949b-ffeecb373939_1692x1260.png 848w, https://substackcdn.com/image/fetch/$s_!PgMu!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36f933b5-4372-4632-949b-ffeecb373939_1692x1260.png 1272w, https://substackcdn.com/image/fetch/$s_!PgMu!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36f933b5-4372-4632-949b-ffeecb373939_1692x1260.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!PgMu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36f933b5-4372-4632-949b-ffeecb373939_1692x1260.png" width="1456" height="1084" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/36f933b5-4372-4632-949b-ffeecb373939_1692x1260.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1084,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:174125,&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/164891498?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36f933b5-4372-4632-949b-ffeecb373939_1692x1260.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_!PgMu!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36f933b5-4372-4632-949b-ffeecb373939_1692x1260.png 424w, https://substackcdn.com/image/fetch/$s_!PgMu!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36f933b5-4372-4632-949b-ffeecb373939_1692x1260.png 848w, https://substackcdn.com/image/fetch/$s_!PgMu!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36f933b5-4372-4632-949b-ffeecb373939_1692x1260.png 1272w, https://substackcdn.com/image/fetch/$s_!PgMu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36f933b5-4372-4632-949b-ffeecb373939_1692x1260.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>Once the app has been approved, you and your teammates can now use the Bot!! </p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!rL5a!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c25ef35-0f48-4d94-9946-0bbc3cc3d55b_1624x361.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!rL5a!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c25ef35-0f48-4d94-9946-0bbc3cc3d55b_1624x361.png 424w, https://substackcdn.com/image/fetch/$s_!rL5a!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c25ef35-0f48-4d94-9946-0bbc3cc3d55b_1624x361.png 848w, https://substackcdn.com/image/fetch/$s_!rL5a!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c25ef35-0f48-4d94-9946-0bbc3cc3d55b_1624x361.png 1272w, https://substackcdn.com/image/fetch/$s_!rL5a!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c25ef35-0f48-4d94-9946-0bbc3cc3d55b_1624x361.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!rL5a!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c25ef35-0f48-4d94-9946-0bbc3cc3d55b_1624x361.png" width="1456" height="324" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3c25ef35-0f48-4d94-9946-0bbc3cc3d55b_1624x361.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:324,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:62742,&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/164891498?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c25ef35-0f48-4d94-9946-0bbc3cc3d55b_1624x361.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_!rL5a!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c25ef35-0f48-4d94-9946-0bbc3cc3d55b_1624x361.png 424w, https://substackcdn.com/image/fetch/$s_!rL5a!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c25ef35-0f48-4d94-9946-0bbc3cc3d55b_1624x361.png 848w, https://substackcdn.com/image/fetch/$s_!rL5a!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c25ef35-0f48-4d94-9946-0bbc3cc3d55b_1624x361.png 1272w, https://substackcdn.com/image/fetch/$s_!rL5a!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c25ef35-0f48-4d94-9946-0bbc3cc3d55b_1624x361.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h1>Summary</h1><p>This process in Slack takes like 20 minutes, and is largely driven based on understanding permissions and where Slack&#8217;s buttons are. Teams makes this process quite complex, by separating out different functionality in a &#8220;Bot&#8221; resource vs permissions in an &#8220;App Registration&#8221; resource, and then requiring you to write a Manifest json to identify all the App&#8217;s information, and not least to require exactly correctly formatted icons for Teams to consume. </p><p>PHEW. </p><p>If you&#8217;ve made it this far, you rule. We&#8217;re going to walk through a great deal of python code in the next few articles, but I think the next one will be about the architecture overall, and what we&#8217;re aiming to achieve. There&#8217;s a great deal of divergence in how Teams APIs work, as well as where data like attachments are stored when shared via Teams, so our code is similarly different. </p><p>Stick around for more. If you want to read the code yourself now, <a href="https://github.com/KyMidd/TeamsAIBot">click here for the repo</a>. </p><p>Good luck out there! <br>kyler</p>]]></content:encoded></item></channel></rss>