<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Denise Ignatova</title>
    <description>The latest articles on DEV Community by Denise Ignatova (@deniseignatova).</description>
    <link>https://dev.to/deniseignatova</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F700789%2F02e0e139-c658-40b2-89d5-5aea89e865a8.png</url>
      <title>DEV Community: Denise Ignatova</title>
      <link>https://dev.to/deniseignatova</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/deniseignatova"/>
    <language>en</language>
    <item>
      <title>DynamoDB Primary Composite Keys - When you find yourself in a situation requiring you to modify a composite sort key</title>
      <dc:creator>Denise Ignatova</dc:creator>
      <pubDate>Wed, 10 Dec 2025 13:51:31 +0000</pubDate>
      <link>https://dev.to/aws-builders/dynamodb-primary-composite-keys-when-you-find-yourself-in-a-situation-requiring-you-to-modify-a-20in</link>
      <guid>https://dev.to/aws-builders/dynamodb-primary-composite-keys-when-you-find-yourself-in-a-situation-requiring-you-to-modify-a-20in</guid>
      <description>&lt;p&gt;Hey wonderful readers and knowledge pursuers! &lt;br&gt;
Today I want to talk about DynamoDB and its composite primary and sort keys. I am 100% aware that there is a huge amount of well written articles and on top of that great and verbose documentation for AWS DynamoDB, that I am using every day. But also we all know that there are situation that are not always described in the books and docs.&lt;br&gt;
With all that said, here I am telling a short story about one of these cases.&lt;br&gt;
Once upon a time a team of architects  designed a software architecture, created according to a well written and coordinated documentation. But a third party player missed to mention a specific key factor and the blue sky gathered clouds. &lt;br&gt;
OK! Enough mystery tales! &lt;br&gt;
The design included use of DynamoDB and one of the tasks was to create a table in  DynamoDB. The design included  primary partition composite key and a sort  key, and also very smart separation - every data adjacent to the main data object was pushed into the Dynamo table as a  separate record, in the code base use Dynamo Toolbox, Schema Validators etc, etc... all goodies available out there. &lt;br&gt;
All done! All set! Start....!&lt;br&gt;
After a while.....&lt;br&gt;
The skipped key factor from the third party, came into a play and was forcing us to change the composite sort key. Changing the sort key programatically in the schema used with Dynamo Toolbox, was a one line easy change. The problem and issue was, that the existing data after we change the sort key would continue to exists with the old sort key and any update operation on the records would fail miserably. We had to find a way to update the sort key on the existing data.&lt;br&gt;
Now! The real story begins.... &lt;br&gt;
Obstacles:  Primary partition keys and sort keys are immutable in DynamoDB - you can not  just run a trivial update and update composite sort key.&lt;br&gt;
Understanding the core components of DynamoDB - Link to AWS documentation&lt;br&gt;
Solution:  The solution we worked out was: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Get the items with the old sort key&lt;/li&gt;
&lt;li&gt;Delete the items with the old sort key &lt;/li&gt;
&lt;li&gt;Re-create the items with the new sort key &lt;/li&gt;
&lt;li&gt;&lt;p&gt;Delete and Put must run all in one transaction&lt;br&gt;
Now the question how to implement:&lt;br&gt;
We decided that best is to run one-time script to fix the discrepancy. &lt;br&gt;
There are two options: &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;create bash script and use AWS CLI commands&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;or use AWS SDK &lt;br&gt;
We have decided to use AWS SDK for JavaScript v3.&lt;br&gt;
And there we go:&lt;br&gt;
Looking at the process , the first thing to do is to extract the records with the old sort key. You can not query,  simply because you do not know the primary key of the affected records, so you need to scan the whole table:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { DynamoDBClient, ScanCommand } from "@aws-sdk/client-dynamodb"; 
import { DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb";
const client = new DynamoDBClient();
const ddbDocClient = DynamoDBDocumentClient.from(client)

const input  = {
    TableName = "your table name",
    FilterExpression: "begins_with(#sk, :sk_portion_you_know)",
    ExpressionAttributeNames: {
        #sk: "sk"
    },
    ExpressionAttributeValues: {
        :sk_portion_you_know: {
          S: "The Portion You know and it is static"
    },

}

const response = await client.send(new ScanCommand(input))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;I know , I know, do not panic. Scan is huge, consumes a lot of reads, but done once it is not an overkill. &lt;br&gt;
Here is the pricing from the AWS DynamoDB documentation link to DynamoDB pricing. Briefly (depends on a region) for around 2 million records scan would cost around just over a $1.00.&lt;br&gt;
So far so good.&lt;br&gt;
Anything I am missing ?&lt;br&gt;
Yes!  And there it is from AWS documentation &lt;a href="https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Scan.html" rel="noopener noreferrer"&gt;link&lt;/a&gt; to AWS documentation.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;A single Scan operation first reads up to the maximum number of items set (if using the Limit parameter) or a maximum of 1 MB of data and then applies any filtering to the results if a FilterExpression is provided.&lt;/code&gt;&lt;br&gt;
With an operation that scans the whole production table,  the payload would exceed 1Mb.&lt;br&gt;
So the next important bit,  is to implement pagination.&lt;br&gt;
To leverage pagination in DynamoDB you need to add few important params in your input.&lt;br&gt;
&lt;code&gt;ExclusiveStartKey&lt;/code&gt;, &lt;code&gt;LastEvaluatedKey&lt;/code&gt;, &lt;code&gt;Limit&lt;/code&gt;. Ok so lets see how would our input would look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const input  = {
    TableName = "your table name",
    FilterExpression: "begins_with(#sk, :sk_portion_you_know)",
    Limit: 100,
    ExpressionAttributeNames: {
        #sk: "sk"
    },
    ExpressionAttributeValues: {
        :sk_portion_you_know: {
          S: "The Portion You know and it is static"
    },
    ExclusiveStartKey: lastEvaluatedKey

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that the input is all set, how we would achieve the pagination. &lt;br&gt;
First lets briefly (it is a complex and a long topic)  explain what is a pagination in DynamoDB: Pagination is the process of sending subsequent requests to continue when a previous request is incomplete. A Query or Scan operation in DynamoDB might return results that are incomplete and require subsequent requests to get the entire result set. &lt;br&gt;
The &lt;code&gt;Limit&lt;/code&gt; parameter  controls the maximum number of items returned per page, optimising throughput and reducing resource consumption. If the value of &lt;code&gt;LastEvaluatedKey&lt;/code&gt; is undefined, the initial set of items will be returned according to the specified limit. However, if a valid value is provided, the query will return a set of items beginning from the value of &lt;code&gt;LastEvaluatedKey&lt;/code&gt;, which contains the primary key of the last evaluated item. &lt;br&gt;
&lt;a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Query.Pagination.html" rel="noopener noreferrer"&gt;Examples&lt;/a&gt; of how the response from scan is constructed and where the LastEvaluatedKey is placed.&lt;br&gt;
As I love to say this can be implemented in a million and one ways, but simply this could also work:&lt;br&gt;
The main thing to take out is that you need to query/scan till you have a LastEvaluatedKey.&lt;br&gt;
So if you are using typescript like me: you can simply do a &lt;code&gt;do while loop&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
do {
    command = await client.send(new ScanCommand(input))
    lastEvaluatedKey = command.LastEvaluateKey
    // update your input's params
    params.lastEvaluatedKey = lastEvaluatedKey
} while (lastEvaluatedKey)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now your scan will have a pointer from which to start the read  for the next bunch of data till there is a data to query/scan - &lt;code&gt;LastEvaluatedKey&lt;/code&gt;.&lt;br&gt;
And your input now would look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; const allRecords: Record&amp;lt;string, AttributeValue&amp;gt; = []
 const transactions: Record&amp;lt;string, AttributeValue&amp;gt; = []
const input  = {
    TableName = "your table name",
    FilterExpression: "begins_with(#sk, :sk_portion_you_know)",
    Limit: 100,
    ExpressionAttributeNames: {
        #sk: "sk"
    },
    ExpressionAttributeValues: {
        :sk_portion_you_know: {
          S: "The Portion You know and it is static"
    },
    ExclusiveStartKey: lastEvaluatedKey

}
 let data:ScanCommandOutput
try {
    do {
        data = await client.send(new ScanCommand(input))
        lastEvaluatedKey = data.LastEvaluateKey
        if (data.Items.length) {
            allRecords.push(...data.Items)
        // update your input's params
        params.lastEvaluatedKey = lastEvaluatedKey
    } while (lastEvaluatedKey);

        if (allRecords.length) {
            allRecords.forEach((item) =&amp;gt; {
            if (item.sk.S === 'your old key') {
                transactions.push(item)
                }
            })
        }
    } catch(error) {
        throw new Error('Error thrown during scan operation')
    }
  }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🚩 There are number of expressions you can use  in &lt;code&gt;FilterExpression&lt;/code&gt; to filter your search. We had to use &lt;code&gt;begins_with&lt;/code&gt;. &lt;a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Query.KeyConditionExpressions.html" rel="noopener noreferrer"&gt;Link&lt;/a&gt; to good examples on how to use different condition expressions&lt;br&gt;
Next lets dive into the last part  - deal with changed Composite Sort Key.&lt;br&gt;
We obviously can not updated it in terms of a trivial update, because composite primary keys are immutable as I already mentioned. We will delete and put back the same record with the new sort key. Lets do it!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function transactWrite(records: Record&amp;lt;string, AttributeValue&amp;gt;[]) {
    records.forEach(async (transaction) =&amp;gt; {
    // we are getting the records via ScanCommand marshalled - item: {S: 'field value'} ,
    // the DynamoDBDocumentClient Put uses high level json - {item: value} and to put them we need to unmarshall
    const unmarshalled = unmarshall(transaction);
    const input = {
        ConditionCheck: {
            TableName: tableName,
            ConditionExpression: 'attribute_exists(pk)',
            ExpressionAttributeNames: {
                '#pk': 'pk',
            },
            ExpressionAttributeValues: {
                ':pk': `${unmarshalled.pk}`,
            },
            Key: {
                pk: `${unmarshalled.pk}`,
                sk: `${unmarshalled.sk}`,
            },
            },
            TransactItems: [
            {
                Delete: {
                    TableName: tableName,
                    Key: {
                    pk: `${unmarshalled.pk}`,
                    sk: `${unmarshalled.sk}`,
                    },
                },
            },
            {
                Put: {
                    TableName: tableName,
                    Item: {
                    ...unmarshalled,
                    sk: `The new key to update with put`,
                    },

                },
            },
            ],
        };
    try {
        const response = await docClient.send(new TransactWriteCommand(input));
        console.log('Item transactWrite successfully.', response);
        } catch (error) {
        console.log('Error transactWrite:', error);
    }
  });
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One very important note here - &lt;code&gt;ScanCommand&lt;/code&gt; will return result &lt;code&gt;marshalled&lt;/code&gt; like item: {S: 'field value'}. You must &lt;code&gt;unmarshall()&lt;/code&gt; the  record before using it in Put. Failed to do so , you will end up with Schema Validation Error - ValidationException The provided key element does not match the schema from AWS SDK simply because Put uses high level json schema to validate the input in this manner {"item": "value"}.&lt;br&gt;
&lt;a href="https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/Package/-aws-sdk-util-dynamodb/#unmarshall-1" rel="noopener noreferrer"&gt;Documentation&lt;/a&gt; with example about &lt;code&gt;marshalling&lt;/code&gt; and &lt;code&gt;unmarshalling&lt;/code&gt; your records.&lt;br&gt;
Having Delete and Put in one Transaction adds a confidence, simply because if something goes wrong , the transaction will not be completed and will rollback.&lt;br&gt;
🤓 Little advice:&lt;br&gt;
✅ It is also good to take a few extra steps to make sure you have the data you want to update in a separate file, to extract modified records in another file , so you can compare the changes along side with the same records, but before the transaction. Again there are multiple ways and different precaution steps to take to make sure everything goes smooth and as planned.&lt;br&gt;
It is not  the greatest experience, but it happens. Sometimes internal documentation not often enough updated and leading to design holes. Often , but no always when other parties involved,  miscommunication is something that could go along, and many other reasons, you name it. &lt;br&gt;
💡 Take off:&lt;br&gt;
The most important as a take off  is that - anything is possible, anything could be fixed, there is always a way, and last but not least , as long as you have a great  team to support you and back you up 💪 - here is the right place to mention that the smart and great design of DynamoDB made it possible, and the extensive and comprehensive guide of AWS SDK.&lt;br&gt;&lt;br&gt;
🚀 A huge Thank you to  AWS DynamoDB team and AWS SDK team. Without the extensive suite of functions from AWS SDK , without the verbose and amazing  examples in the documentation for AWS SDK we would have been lost and stuck into a forest of trials and errors.&lt;br&gt;
 🌟 Thank you to you my dear reader. Thank you for sticking with me to the end. I hope this could help other people in a similar or same situation.&lt;br&gt;
Let's connect 🤝&lt;br&gt;
LinkedIn&lt;br&gt;
✍ Would love to hear from you ✍&lt;/p&gt;

</description>
      <category>serverless</category>
      <category>dynamodb</category>
      <category>microservices</category>
    </item>
    <item>
      <title>MCP Custom Server Tools + Amazon-Q. How to ...?</title>
      <dc:creator>Denise Ignatova</dc:creator>
      <pubDate>Sun, 08 Jun 2025 19:10:55 +0000</pubDate>
      <link>https://dev.to/aws-builders/mcp-custom-server-tools-amazon-q-how-to--12k</link>
      <guid>https://dev.to/aws-builders/mcp-custom-server-tools-amazon-q-how-to--12k</guid>
      <description>&lt;p&gt;Hey fellow reader!&lt;br&gt;
It is me again , the one that always tries to catch the running train.&lt;br&gt;
Once I tasted the power of MCP protocol, I wanted more...&lt;br&gt;
And here it is, short post about how to implement and use MCP tools with Amazon-Q or any other client.&lt;br&gt;
If you have followed my previous post on how to implement MCP custom server &lt;a href="https://dev.to/deniseignatova/custom-mcp-server-amazon-q-powerhouse-combination-3a0n"&gt;this one&lt;/a&gt;, you can use the same project and we can get going from there.&lt;br&gt;
Few words on what MCP Server tools are :&lt;br&gt;
There is an excellent documentation &lt;a href="https://modelcontextprotocol.io/docs/concepts/tools" rel="noopener noreferrer"&gt;MCP protocol tools&lt;/a&gt;, and in short, and as a really high overview - Tools as primitives of MCP protocol allow the server to expose executable functionality to clients.&lt;br&gt;
In our case, with a custom server, it would mean that we would be able to ask our LLM client in a natural language to look into our custom server, find the tool and execute it. Wow! &lt;br&gt;
Lets get to work...&lt;br&gt;
We have our MCP custom server exposing resources and connected to Amazon-Q, all what we need to do is to add new capability: tools.&lt;br&gt;
Our tool will be able to generate image file from a flowchart created with mermaid markdown. We will be using mermaid.js API call to generate output image from input mermaid markdown. &lt;a href="https://mermaid.js.org/config/usage.html" rel="noopener noreferrer"&gt;Mermaid API Usage&lt;/a&gt; Cool!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Server&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@modelcontextprotocol/sdk/server/index.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;StdioServerTransport&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@modelcontextprotocol/sdk/server/stdio.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CallToolRequestSchema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ListResourcesRequestSchema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ListToolsRequestSchema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ReadResourceRequestSchema&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@modelcontextprotocol/sdk/types.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;dirname&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;fileURLToPath&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:url&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@mermaid-js/mermaid-cli&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fs/promises&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;docs-demo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1.0.0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;capabilities&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
            &lt;span class="na"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again we will use low-server methods to create our tool. Add this to index.ts&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setRequestHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ListToolsRequestSchema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;flow-chart&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Display flow chart with mermaid&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;inputSchema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                   &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
                   &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;},&lt;/span&gt;
                &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;input&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;output&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
             &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Flow chart views&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setRequestHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CallToolRequestSchema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;flow-chart&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.png`&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.md`&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.markdown`&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.svg`&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.pdf`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;file://&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image/png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Tool not found&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we need to check if our client Amazon-Q is connected to our server and would be able to find our server tool.&lt;/p&gt;

&lt;p&gt;In the terminal type this after starting the client with &lt;code&gt;q chat&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/tools
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;you should be able to see this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3y5lt7vtpt4nqd2njpv7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3y5lt7vtpt4nqd2njpv7.png" alt=" " width="800" height="228"&gt;&lt;/a&gt;&lt;br&gt;
Note: for this example we are interested in the flowchart tool.&lt;br&gt;
Now lets ask our client Amazon-Q in a natural language to generate image in a specified directory from a flowchart.md located in our docs folder using our custom MCP server's tool flowchart.&lt;br&gt;
&lt;code&gt;could you generate image file from ./src/docs/flowchart.md in ./src/assets/chart.png using flowchart tool from mcp server.&lt;/code&gt;&lt;br&gt;
You should be able to see this: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffo5wqj30v2r61y4fxu2i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffo5wqj30v2r61y4fxu2i.png" alt=" " width="800" height="372"&gt;&lt;/a&gt;&lt;br&gt;
type &lt;code&gt;t&lt;/code&gt; in the terminal to confirm you trust your tool and to allow further actions.&lt;br&gt;
And there we have it:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffplxc3hdrlbwjm4p7fgr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffplxc3hdrlbwjm4p7fgr.png" alt=" " width="800" height="166"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I am extremely excited about what the future holds. &lt;br&gt;
Thank you for reading. I am beyond grateful for your time and will appreciate your comments and feedback highly.&lt;br&gt;
Can not wait to find out, what awesome tools you have built, using MCP protocol and custom server.&lt;br&gt;
Happy building! &lt;/p&gt;

&lt;p&gt;You can wonder through the &lt;a href="https://github.com/dignat/mcp-server" rel="noopener noreferrer"&gt;Git repo&lt;/a&gt; as well.&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>aws</category>
      <category>ai</category>
      <category>programming</category>
    </item>
    <item>
      <title>Custom MCP Server + Amazon Q - Powerhouse combination</title>
      <dc:creator>Denise Ignatova</dc:creator>
      <pubDate>Fri, 23 May 2025 13:28:12 +0000</pubDate>
      <link>https://dev.to/aws-builders/custom-mcp-server-amazon-q-powerhouse-combination-3a0n</link>
      <guid>https://dev.to/aws-builders/custom-mcp-server-amazon-q-powerhouse-combination-3a0n</guid>
      <description>&lt;p&gt;Hey fellow reader!&lt;br&gt;
I hope am not too late for the MCP party, and I hope with this short post to add meaningful contribution to the already awesome resources out there.&lt;br&gt;
In short:&lt;br&gt;
I have decided to try out building custom MCP server which exposes as a resource specific documentation and connect it Amazon Q.&lt;br&gt;
I have used typescript sdk &lt;a href="https://github.com/modelcontextprotocol/typescript-sdk?tab=readme-ov-file" rel="noopener noreferrer"&gt;Link here&lt;/a&gt;.&lt;br&gt;
Lets get started then!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;create a directory
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir mcp-server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;(side note: I am using &lt;code&gt;pnpm&lt;/code&gt;, as package manager)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;inside the newly created directory run
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pnpm init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;to generate package.json file. Once it is generated, navigate to it and replace script section with :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;script: {
    "build": "tsc &amp;amp;&amp;amp; chmod 755 build/index.js"
  }

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;you are pointing typescript where to store your public endpoint , giving the necessary permission too. After that you would need to create your tsconfig.json.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tsc init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;make the needed changes: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;outDir: './build'&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;target: 'es2022'&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;module: 'NodeNext'&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;last two would allow top level &lt;code&gt;await&lt;/code&gt; to be used.&lt;br&gt;
Install &lt;code&gt;@modelcontextprotocol/sdk&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We will be using low-level server. Examples from the mcp repository : &lt;a href="https://github.com/modelcontextprotocol/typescript-sdk?tab=readme-ov-file#low-level-server" rel="noopener noreferrer"&gt;Low-level mcp-server&lt;/a&gt;.&lt;br&gt;
We can start.&lt;br&gt;
Create a &lt;code&gt;src&lt;/code&gt; directory and inside it create a file called &lt;code&gt;index.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Server&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@modelcontextprotocol/sdk/server/index.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;StdioServerTransport&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@modelcontextprotocol/sdk/server/stdio.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ListResourcesRequestSchema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ReadResourceRequestSchema&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@modelcontextprotocol/sdk/types.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:fs/promises&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;dirname&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;fileURLToPath&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:url&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;__filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fileURLToPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;__dirname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__filename&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;docs-demo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1.0.0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;capabilities&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
            &lt;span class="na"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;docFiles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./src/docs/integration.md&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setRequestHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ListResourcesRequestSchema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;:[&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;file://&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;__dirname&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/docs/integration.md&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Integration docs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;mimeType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text/plain&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setRequestHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ReadResourceRequestSchema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;file://&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;__dirname&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/docs/integration.md&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nx"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;mimeType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text/plain&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;docFiles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf-8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Resource not found&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;


&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transposrt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;StdioServerTransport&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Starting server...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transposrt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a &lt;code&gt;docs&lt;/code&gt; folder in you &lt;code&gt;src&lt;/code&gt; folder. There we will add our integration.md file. You would need an &lt;code&gt;assets&lt;/code&gt; folder with our diagram. You can download this one!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fejun87nize07yqu1jj1q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fejun87nize07yqu1jj1q.png" alt="Image description" width="800" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;content of integratiion.md file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Integration awesome105
### Build a simple flow with AWS SQS , AWS SNS and AWS Lambda

![Diagram](../assets/image.png)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we need to install Amazon-q CLI. Here is an excellent tutorial how to install amazon-q on a different os systems.&lt;br&gt;
&lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html" rel="noopener noreferrer"&gt;Link to the tutorial&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;I have used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;brew install amazon-q
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After installation check if it is installed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;q version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now comes the most important part. To let know amazon-q that there is a mcp-server. Amazon-q will be installed in a directory &lt;code&gt;~/.aws/amazonq&lt;/code&gt;. Get into this directory like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd ~/.aws/amazonq
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now type &lt;code&gt;ls&lt;/code&gt; to list everything in this directory - should not have &lt;code&gt;mcp.json&lt;/code&gt;. Type in the terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;touch mcp.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;copy and paste this and save:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"docs-demo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;command&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;that&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;will&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;run&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;your&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;index.js&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"/absolute_path/build/index.js"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;//absolute&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;your&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;index.js&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now time to test!&lt;br&gt;
Go back to your terminal and type &lt;code&gt;q chat&lt;/code&gt;. You should be able to see this .... &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqk4vuvtc7fov7mq57nx1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqk4vuvtc7fov7mq57nx1.png" alt="Image description" width="800" height="46"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now lets ask amazon-q to find documentation for our &lt;code&gt;awesome105&lt;/code&gt; integration and explain the diagram associated with it.&lt;/p&gt;

&lt;p&gt;And voila...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fblgc60zqj4zvpnp2yr4y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fblgc60zqj4zvpnp2yr4y.png" alt="Image description" width="800" height="461"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is truly amazing! Could you imagine what horizons this feature opens? For me beyond my dreams! Thank you Amazon-q!&lt;br&gt;
Thank you for reading! Please leave a comment and let me know what cool projects you came up with using mcp-server and amazon-q!&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>programming</category>
    </item>
    <item>
      <title>AWS SQS and fan-out pattern with Kafka (Confluent).</title>
      <dc:creator>Denise Ignatova</dc:creator>
      <pubDate>Wed, 21 May 2025 12:57:47 +0000</pubDate>
      <link>https://dev.to/aws-builders/aws-sqs-and-fan-out-pattern-with-kafka-confluent-2jph</link>
      <guid>https://dev.to/aws-builders/aws-sqs-and-fan-out-pattern-with-kafka-confluent-2jph</guid>
      <description>&lt;p&gt;Refactoring !!! &lt;br&gt;
Refactoring is inevitable. We face it almost every day. Sometimes we plan for it, sometimes comes as an unscheduled change.&lt;/p&gt;

&lt;p&gt;At the beginning of the story, we had a simple, easy flow.&lt;br&gt;
Very trivial workflow: &lt;br&gt;
Publisher will publish a message to AWS SNS Topic , which is subscribed to AWS SQS , then the AWS SQS will send a message to Kafka. So far so good:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fir3ffwve9k7nswc37hme.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fir3ffwve9k7nswc37hme.png" alt="Image description" width="800" height="378"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;with schema matching the structure of the domain product, to push to Kafka. Something along the lines in json format:&lt;/p&gt;

&lt;p&gt;JSONSchema (if you are interested to find out more)&lt;br&gt;
&lt;a href="https://json-schema.org/learn/getting-started-step-by-step" rel="noopener noreferrer"&gt;JSONSchema&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://example.com/product.schema.json",
  "title": "Product",
  "description": "A product from Acme's catalog",
  "type": "object",
  "properties": {
    "productId": {
      "description": "The unique identifier for a product",
      "type": "integer"
    }
  }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In order to satisfy the new business requirements, we came up with a solution involving changing the structure of the schema for our domain event.&lt;br&gt;
Something along the lines: leveraging the &lt;code&gt;oneOf&lt;/code&gt; feature to create complex tree like structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
 "$schema": "http://json-schema.org/draft-07/schema#",
 "$id": "TreeNode",
 "type": "object",
 "properties": {
   "value": { "type": "number" },
   "left": {
     "oneOf": [
       { "type": "null" },
       { "$ref": "TreeNode" }
     ]
   },
   "right": {
     "oneOf": [
       { "type": "null" },
       { "$ref": "TreeNode" }
     ]
   }
 },
 "required": ["value", "left", "right"]
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How the refactoring lead to Fan-out pattern?&lt;/p&gt;

&lt;p&gt;It is a well known fact that Kafka Schema Registry have a few compatibility modes and the recommended one is backward compatibility and this is the one we have signed for and it is the default one as well - simply accept new only optional fields, delete fields and do not allow change of type of existing ones. The consumer always read the latest version of the schema and not the version it was produced with. With the huge difference in the schema structure, that was going to be introduced, the crash and the errors were just waiting to happen. &lt;/p&gt;

&lt;p&gt;Kafka Schema Registry compatibility modes.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F76i2fjphys5o4xq843yh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F76i2fjphys5o4xq843yh.png" alt="Image description" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The only solution that could have helped us to maintain the current flat schema and gradually to introduce and accommodate the new event tree-like schema was to use the Fan-out pattern.&lt;/p&gt;

&lt;p&gt;In Fan-out messaging scenario, messages are "pushed" to multiple subscribers, which eliminates the need to periodically check or poll for updates and enables parallel asynchronous processing of the message by the subscribers.&lt;br&gt;
We were aiming at that parallel processing scenario, where our AWS SQS is subscribed to two AWS SNS topics, and our AWS SQS queue is attached as a trigger to an AWS Lambda, which pushes messages to these Kafka topics. Then AWS Lambda subscriber reads the Kafka messages from the two separate Kafka topics, and programmatically decides how to consume the messages depend on the message body structure. As a side note - Kafka topic works as a queue. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2dez50tfwm5v7h3nl2a8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2dez50tfwm5v7h3nl2a8.png" alt="Image description" width="800" height="372"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;How would you achieve this?&lt;br&gt;
You would start with AWS AppConfig: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Create a feature flag - either through the AWS Console or with your favorite serverless framework or simply by using AWS CDK.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create publisher (AWS Lambda).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add Logic to your publisher to decide to which topic to publish message depending on the feature flag.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Create AWS SNS topics.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Create two separate Kafka topics.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Create AWS SQS Fifo queue.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Subscribe the queue to the SNS topics.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Create your producer AWS Lambda.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Attach the AWS SQS as a trigger.&lt;/li&gt;
&lt;li&gt;Bind to the AWS Lambda your Kafka topics.&lt;/li&gt;
&lt;li&gt;Add logic to your producer to read the message body and decide which one to push to which Kafka topic depending on the message structure. &lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Create AWS Lambda as Kafka subscriber ( for your Kafka topics ).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add event source to your Lambda - the Kafka topics. &lt;/li&gt;
&lt;li&gt;Add logic to your code to read and consume the message body from Kafka topic and decide how to process further depending on the message structure. &lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Keeping two separate Kafka topics, allowed us to have the current codebase and flow working uninterrupted and at the same time gave us the freedom to test the new schema without causing any problems in production stage.&lt;br&gt;
Once satisfied with the test results we have been able to perform canary deployment and this approach saved us from the headache and worry about the availability of our product.&lt;/p&gt;

&lt;p&gt;This is it!&lt;br&gt;
As a side Note: You could skip the creation of the two separate AWS SNS topics and by only leveraging the AWS AppConfig feature flag to switch on and off what type of message to flow trough the SNS topic, and keep only the two Kafka topics (this is a must).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3ke54dw2lby7qpa014u3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3ke54dw2lby7qpa014u3.png" alt="Image description" width="800" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thank you for reading! I hope you enjoy it and find it useful. Please leave a comment, would love to hear your thoughts.&lt;/p&gt;

</description>
      <category>serverless</category>
      <category>cloud</category>
      <category>cloudcomputing</category>
      <category>cloudnative</category>
    </item>
    <item>
      <title>Recognizing Design Patterns in System Design - Facade pattern, AWS API Gateway and Load Balancer</title>
      <dc:creator>Denise Ignatova</dc:creator>
      <pubDate>Fri, 17 Jan 2025 15:43:21 +0000</pubDate>
      <link>https://dev.to/deniseignatova/recognizing-design-patterns-in-system-design-facade-pattern-and-aws-api-gateway-516d</link>
      <guid>https://dev.to/deniseignatova/recognizing-design-patterns-in-system-design-facade-pattern-and-aws-api-gateway-516d</guid>
      <description>&lt;p&gt;Making sense of the structure of your code and seeing the big picture has been a goal for every developer ( at least every one I know ), and I am not an exception.&lt;br&gt;
I have been struggling to see from above, to have an overview of the projects I have been working on, and this was affecting my confidence and my productiveness as a developer. &lt;br&gt;
Overwhelmed and certain that I need to go back to the basics, and thanks to the incredible girls from Women Coding Community and their amazing design-patterns course, I started reading again "Head First Design Patterns" by Eric Freeman and  Elisabeth Robson - &lt;a href="https://www.amazon.co.uk/Head-First-Design-Patterns-Object-Oriented/dp/149207800X/ref=asc_df_149207800X?mcid=afda41f639603daf9e724ad16d08573f&amp;amp;tag=googshopuk-21&amp;amp;linkCode=df0&amp;amp;hvadid=697344809240&amp;amp;hvpos=&amp;amp;hvnetw=g&amp;amp;hvrand=13879067684842267942&amp;amp;hvpone=&amp;amp;hvptwo=&amp;amp;hvqmt=&amp;amp;hvdev=c&amp;amp;hvdvcmdl=&amp;amp;hvlocint=&amp;amp;hvlocphy=9045340&amp;amp;hvtargid=pla-918195320150&amp;amp;psc=1&amp;amp;gad_source=1" rel="noopener noreferrer"&gt;link to the book&lt;/a&gt; and consulting with the amazing refactoring guru site &lt;a href="https://refactoring.guru/design-patterns" rel="noopener noreferrer"&gt;link to the site&lt;/a&gt;.&lt;br&gt;
Learning and searching for parallels between the existing and well-known design patterns and modern system designs.&lt;/p&gt;

&lt;p&gt;One design pattern that is very easy recognizable is Facade pattern.&lt;br&gt;
What do we know about Facade pattern? &lt;br&gt;
&lt;strong&gt;Facade pattern&lt;/strong&gt; is a member of so called Structural patterns. It provides a simple interface for a complex logic. What would that mean? This means that you would have one point of contact in your codebase, that behind the scene distributes the logic to the relevant parts of your project.&lt;br&gt;
Real world example image taken from &lt;a href="https://refactoring.guru/design-patterns/facade" rel="noopener noreferrer"&gt;link to facade pattern&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsv8yq6xofgzo2ha77yn6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsv8yq6xofgzo2ha77yn6.png" alt="Image description" width="800" height="310"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now the &lt;strong&gt;Question&lt;/strong&gt;, how this Facade pattern relates to a System Design that we know and probably use everyday.(Drum rolls)....&lt;/p&gt;

&lt;p&gt;AWS API Gateway:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdbfxit7ddjuhsu9dsa4q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdbfxit7ddjuhsu9dsa4q.png" alt="Image description" width="800" height="352"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/welcome.html" rel="noopener noreferrer"&gt;link AWS API Gateway&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;An Obvious winner...!&lt;br&gt;
Can you see the similarities in both images? &lt;br&gt;
Can you clearly see the Facade in the illustrated situation?&lt;br&gt;
Did you see how AWS API Gateway offers a simple interface and hides the complexity of handling multiple calls and redirecting them to the relevant consumer?&lt;br&gt;
The diagram shows how AWS API Gateway is used to distribute and handle different API calls by being that one point of a contact. &lt;br&gt;
In this line of thoughts (light-bulb), wait a second..., what about the Load Balancer:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbvy14a4huyzbaxjszl07.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbvy14a4huyzbaxjszl07.png" alt="Image description" width="555" height="243"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/elasticloadbalancing/latest/application/introduction.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/elasticloadbalancing/latest/application/introduction.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is also an example of providing single entry point of contact( simple interface) , that hides behind a complex logic.&lt;/p&gt;

&lt;p&gt;For me this was an eye opener, and realizing that I can re-learn and re-use this knowledge was pivotal moment and breaking change in my understanding of system design and design patterns.&lt;br&gt;
The benefits of using API Gateway and Load Balancer are countless, and this short post is not about this. It is about how to make use of design patterns knowledge to apply effective and optimal decisions while building and designing systems in the modern serverless world.&lt;br&gt;
Seeing the similarities, recognizing the patterns is a powerful capability. Pumped with this knowledge, you would be able to write better code, to architect better design systems. &lt;/p&gt;

&lt;p&gt;Thank you for reading!&lt;br&gt;
Excited to read your feedback and comments!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How to Debug your AWS Lambda - SST framework and Powertools</title>
      <dc:creator>Denise Ignatova</dc:creator>
      <pubDate>Fri, 17 Jan 2025 11:47:48 +0000</pubDate>
      <link>https://dev.to/deniseignatova/how-to-debug-your-aws-lambda-sst-framework-426a</link>
      <guid>https://dev.to/deniseignatova/how-to-debug-your-aws-lambda-sst-framework-426a</guid>
      <description>&lt;p&gt;When using &lt;strong&gt;serverless&lt;/strong&gt; framework to build your applications, you depend on the framework's capability to achieve your goals. &lt;br&gt;
One of the main parts of the work process is the ability to debug your code, while you build your logic and in case there are some disruptions in the flow.&lt;br&gt;
When working with AWS Lambda and building your application with SST framework, you can add logs around the logic of your Lambda , but also you need to bare in mind that this constant build up of logs could overwhelm AWS CloudWatch and increase your bills.&lt;br&gt;
While developing there is this amazing &lt;strong&gt;Live lambda&lt;/strong&gt; feature provided by the SST framework which allows you to debug , test and invoke live your AWS Lambda by Postman and whatever endpoint(HTTP, GraphQL) you have chosen.&lt;br&gt;
Once in production and already live, your codebase needs more gentle logging approach. One of the possible solutions is the following:&lt;br&gt;
There are two essentials steps to be completed.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Create custom log method in your code base&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add environment variable to the lambda you are interested to debug &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Lets code:&lt;br&gt;
Basically you would have a custom log logic in your codebase, which will be invoked in places.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Logger } from '@aws-lambda-powertools/logger';
const logger = new Logger({ serviceName: 'serverless' });

// use logger to create logs at different level 
// example:
// logger.info()
// logger.debug()
// logger.error()
function myDebug (message: string, ...args: unknown[]) { 
const loggerLevel = logger.getLevelName();
if (loggerLevel === 'DEBUG') {
  // log context
  logger.debug(message, context: {context: args});
 }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then in your Lambda Function snippet you would have environment variable POWERTOOLS_LOG_LEVEL with value INFO.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;new Function(stack, "MyFunction", {
  handler: "src/lambda.handler",
  timeout: 10,
  environment: {
    POWERTOOLS_LOG_LEVEL: "INFO",
  },
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Like that, whenever you need to debug you would need to change via the AWS Console (no need of deploy) the value of your POWERTOOLS_LOG_LEVEL environmental variable to DEBUG and voila on a  so called &lt;code&gt;live&lt;/code&gt; Lambda invocation via SST command &lt;code&gt;sst dev&lt;/code&gt; you would run live your AWS Lambda and have beautiful logs on AWS CloudWatch and your terminal from your code base.&lt;br&gt;
To edit the value of your AWS Lambda's environment variable, choose the Configuration tab, on the Lambda Function page, under Environment variables choose &lt;strong&gt;Edit&lt;/strong&gt;. Once you have changed the value choose &lt;strong&gt;Save&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Thank you for reading!&lt;br&gt;
Excited to see any feedback!&lt;br&gt;
Have an amazing journey in cloud world!&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
