Skip to content

Commit d48e0c0

Browse files
JonLimJonathan Lim
andauthored
feat(aws-documentation-mcp-server): Add search intent capturing (#1918)
* feature(aws-documentation-mcp-server): Adding ability to capture search intent for AWS Docs MCP Server * Renaming intent to search_intent, fixing filter for adding to queryparams --------- Co-authored-by: Jonathan Lim <[email protected]>
1 parent 2c368b7 commit d48e0c0

File tree

3 files changed

+208
-0
lines changed

3 files changed

+208
-0
lines changed

src/aws-documentation-mcp-server/awslabs/aws_documentation_mcp_server/server_aws.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232

3333
# Import utility functions
3434
from awslabs.aws_documentation_mcp_server.util import (
35+
add_search_intent_to_search_request,
3536
parse_recommendation_results,
3637
)
3738
from loguru import logger
@@ -165,6 +166,10 @@ async def read_documentation(
165166
async def search_documentation(
166167
ctx: Context,
167168
search_phrase: str = Field(description='Search phrase to use'),
169+
search_intent: str = Field(
170+
description='For the search_phrase parameter, describe the search intent of the user. CRITICAL: Do not include any PII or customer data, describe only the AWS-related intent for search.',
171+
default='',
172+
),
168173
limit: int = Field(
169174
default=10,
170175
description='Maximum number of results to return',
@@ -215,6 +220,7 @@ async def search_documentation(
215220
Args:
216221
ctx: MCP context for logging and error handling
217222
search_phrase: Search phrase to use
223+
search_intent: The intent behind the search requested by the user
218224
limit: Maximum number of results to return
219225
product_types: Filter by AWS product/service
220226
guide_types: Filter by guide type
@@ -249,6 +255,9 @@ async def search_documentation(
249255
)
250256

251257
search_url_with_session = f'{SEARCH_API_URL}?session={SESSION_UUID}'
258+
search_url_with_session = add_search_intent_to_search_request(
259+
search_url_with_session, search_intent
260+
)
252261

253262
async with httpx.AsyncClient() as client:
254263
try:

src/aws-documentation-mcp-server/awslabs/aws_documentation_mcp_server/util.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import markdownify
1717
from awslabs.aws_documentation_mcp_server.models import RecommendationResult
1818
from typing import Any, Dict, List
19+
from urllib.parse import quote_plus
1920

2021

2122
def extract_content_from_html(html: str) -> str:
@@ -255,3 +256,23 @@ def parse_recommendation_results(data: Dict[str, Any]) -> List[RecommendationRes
255256
)
256257

257258
return results
259+
260+
261+
def add_search_intent_to_search_request(search_url: str, search_intent: str) -> str:
262+
"""Adds the search_intent query parameter to the search_url if search_intent is a string.
263+
264+
:param search_url: URL to be used for search_documentation tool call
265+
:type search_url: str
266+
:param search_intent: Intent derived and provided by LLM to MCP Server for user's search intent
267+
:type search_intent: str
268+
:return: search_url with search_intent query parameter added
269+
:rtype: str
270+
"""
271+
if search_intent and search_intent != '':
272+
# Remove all whitespaces, including tabs and returns
273+
search_intent = ' '.join(f'{search_intent}'.split())
274+
if search_intent:
275+
encoded_search_intent = quote_plus(search_intent)
276+
search_url = f'{search_url}&search_intent={encoded_search_intent}'
277+
278+
return search_url

src/aws-documentation-mcp-server/tests/test_util.py

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
import os
1717
from awslabs.aws_documentation_mcp_server.util import (
18+
add_search_intent_to_search_request,
1819
extract_content_from_html,
1920
format_documentation_result,
2021
is_html_content,
@@ -377,3 +378,180 @@ def test_all_recommendation_types(self):
377378
assert 'https://docs.aws.amazon.com/journey' in urls
378379
assert 'https://docs.aws.amazon.com/new' in urls
379380
assert 'https://docs.aws.amazon.com/similar' in urls
381+
382+
383+
class TestAddSearchIntentToSearchRequest:
384+
"""Tests for add_search_intent_to_search_request function."""
385+
386+
def test_valid_search_intent_simple(self):
387+
"""Test adding a simple valid search intent."""
388+
search_url = 'https://docs.aws.amazon.com/search'
389+
search_intent = 'how to deploy'
390+
result = add_search_intent_to_search_request(search_url, search_intent)
391+
assert result == 'https://docs.aws.amazon.com/search&search_intent=how+to+deploy'
392+
393+
def test_valid_search_intent_with_special_chars(self):
394+
"""Test adding search intent with special characters that need URL encoding."""
395+
search_url = 'https://docs.aws.amazon.com/search'
396+
search_intent = 'how to configure S3 bucket & policies?'
397+
result = add_search_intent_to_search_request(search_url, search_intent)
398+
# quote_plus should encode spaces as '+' and special chars as '%XX'
399+
assert (
400+
result
401+
== 'https://docs.aws.amazon.com/search&search_intent=how+to+configure+S3+bucket+%26+policies%3F'
402+
)
403+
404+
def test_valid_search_intent_with_unicode(self):
405+
"""Test adding search intent with unicode characters."""
406+
search_url = 'https://docs.aws.amazon.com/search'
407+
search_intent = 'déployer une instance'
408+
result = add_search_intent_to_search_request(search_url, search_intent)
409+
# Verify the URL is properly encoded (unicode characters should be percent-encoded)
410+
assert (
411+
result == 'https://docs.aws.amazon.com/search&search_intent=d%C3%A9ployer+une+instance'
412+
)
413+
414+
def test_valid_search_intent_with_multiple_words(self):
415+
"""Test adding search intent with multiple words."""
416+
search_url = 'https://docs.aws.amazon.com/search'
417+
search_intent = 'create table with provisioned throughput'
418+
result = add_search_intent_to_search_request(search_url, search_intent)
419+
assert (
420+
result
421+
== 'https://docs.aws.amazon.com/search&search_intent=create+table+with+provisioned+throughput'
422+
)
423+
424+
def test_valid_search_intent_with_slashes(self):
425+
"""Test adding search intent with forward slashes."""
426+
search_url = 'https://docs.aws.amazon.com/search'
427+
search_intent = 'REST/HTTP API configuration'
428+
result = add_search_intent_to_search_request(search_url, search_intent)
429+
# Forward slashes should be encoded as %2F
430+
assert (
431+
result
432+
== 'https://docs.aws.amazon.com/search&search_intent=REST%2FHTTP+API+configuration'
433+
)
434+
435+
def test_empty_search_intent(self):
436+
"""Test with empty string search intent (should not add parameter)."""
437+
search_url = 'https://docs.aws.amazon.com/search'
438+
search_intent = ''
439+
result = add_search_intent_to_search_request(search_url, search_intent)
440+
assert result == 'https://docs.aws.amazon.com/search'
441+
assert 'search_intent' not in result
442+
443+
def test_whitespace_only_search_intent(self):
444+
"""Test with whitespace-only search intent (should add parameter with encoded spaces)."""
445+
search_url = 'https://docs.aws.amazon.com/search'
446+
search_intent = ' '
447+
result = add_search_intent_to_search_request(search_url, search_intent)
448+
# Whitespace is truthy, so it should be added
449+
assert result == 'https://docs.aws.amazon.com/search'
450+
451+
def test_search_intent_with_numbers(self):
452+
"""Test adding search intent with numbers."""
453+
search_url = 'https://docs.aws.amazon.com/search'
454+
search_intent = 'configure RDS with 1000 IOPS'
455+
result = add_search_intent_to_search_request(search_url, search_intent)
456+
assert (
457+
result
458+
== 'https://docs.aws.amazon.com/search&search_intent=configure+RDS+with+1000+IOPS'
459+
)
460+
461+
def test_search_intent_with_punctuation(self):
462+
"""Test adding search intent with various punctuation marks."""
463+
search_url = 'https://docs.aws.amazon.com/search'
464+
search_intent = 'metrics, alarms & dashboards - how-to guide!'
465+
result = add_search_intent_to_search_request(search_url, search_intent)
466+
assert (
467+
result
468+
== 'https://docs.aws.amazon.com/search&search_intent=metrics%2C+alarms+%26+dashboards+-+how-to+guide%21'
469+
)
470+
471+
def test_very_long_search_intent(self):
472+
"""Test adding a very long search intent."""
473+
search_url = 'https://docs.aws.amazon.com/search'
474+
search_intent = 'how to create and configure an AWS Lambda function with VPC access and custom IAM roles for processing S3 events'
475+
result = add_search_intent_to_search_request(search_url, search_intent)
476+
expected = 'https://docs.aws.amazon.com/search&search_intent=how+to+create+and+configure+an+AWS+Lambda+function+with+VPC+access+and+custom+IAM+roles+for+processing+S3+events'
477+
assert result == expected
478+
479+
def test_search_intent_with_equals_sign(self):
480+
"""Test adding search intent with equals sign."""
481+
search_url = 'https://docs.aws.amazon.com/search'
482+
search_intent = 'set parameter=value'
483+
result = add_search_intent_to_search_request(search_url, search_intent)
484+
# Equals sign should be encoded as %3D
485+
assert result == 'https://docs.aws.amazon.com/search&search_intent=set+parameter%3Dvalue'
486+
487+
def test_search_intent_with_tab_character(self):
488+
"""Test adding search intent with tab character."""
489+
search_url = 'https://docs.aws.amazon.com/search'
490+
search_intent = 'tab\tcharacter'
491+
result = add_search_intent_to_search_request(search_url, search_intent)
492+
# Tab should be encoded as %09
493+
assert result == 'https://docs.aws.amazon.com/search&search_intent=tab+character'
494+
495+
def test_search_intent_with_newline(self):
496+
"""Test adding search intent with newline character."""
497+
search_url = 'https://docs.aws.amazon.com/search'
498+
search_intent = 'line\nbreak'
499+
result = add_search_intent_to_search_request(search_url, search_intent)
500+
# Newline should be encoded as %0A
501+
assert result == 'https://docs.aws.amazon.com/search&search_intent=line+break'
502+
503+
def test_search_intent_with_carriage_return(self):
504+
"""Test adding search intent with carriage return."""
505+
search_url = 'https://docs.aws.amazon.com/search'
506+
search_intent = 'carriage\rreturn'
507+
result = add_search_intent_to_search_request(search_url, search_intent)
508+
# Carriage return should be encoded as %0D
509+
assert result == 'https://docs.aws.amazon.com/search&search_intent=carriage+return'
510+
511+
def test_search_intent_with_hash(self):
512+
"""Test adding search intent with hash/pound sign."""
513+
search_url = 'https://docs.aws.amazon.com/search'
514+
search_intent = 'C# programming'
515+
result = add_search_intent_to_search_request(search_url, search_intent)
516+
# Hash should be encoded as %23
517+
assert result == 'https://docs.aws.amazon.com/search&search_intent=C%23+programming'
518+
519+
def test_search_intent_with_percent_sign(self):
520+
"""Test adding search intent with percent sign."""
521+
search_url = 'https://docs.aws.amazon.com/search'
522+
search_intent = '100% CPU usage'
523+
result = add_search_intent_to_search_request(search_url, search_intent)
524+
# Percent sign should be encoded as %25
525+
assert result == 'https://docs.aws.amazon.com/search&search_intent=100%25+CPU+usage'
526+
527+
def test_search_intent_with_plus_sign(self):
528+
"""Test adding search intent with plus sign."""
529+
search_url = 'https://docs.aws.amazon.com/search'
530+
search_intent = 'C++'
531+
result = add_search_intent_to_search_request(search_url, search_intent)
532+
# Plus sign should be encoded as %2B
533+
assert result == 'https://docs.aws.amazon.com/search&search_intent=C%2B%2B'
534+
535+
def test_search_intent_with_ampersand(self):
536+
"""Test adding search intent with ampersand."""
537+
search_url = 'https://docs.aws.amazon.com/search'
538+
search_intent = 'S3 & EC2'
539+
result = add_search_intent_to_search_request(search_url, search_intent)
540+
# Ampersand should be encoded as %26
541+
assert result == 'https://docs.aws.amazon.com/search&search_intent=S3+%26+EC2'
542+
543+
def test_search_intent_with_question_mark(self):
544+
"""Test adding search intent with question mark."""
545+
search_url = 'https://docs.aws.amazon.com/search'
546+
search_intent = 'what is lambda?'
547+
result = add_search_intent_to_search_request(search_url, search_intent)
548+
# Question mark should be encoded as %3F
549+
assert result == 'https://docs.aws.amazon.com/search&search_intent=what+is+lambda%3F'
550+
551+
def test_url_with_existing_parameters(self):
552+
"""Test that the function appends to existing URL structure."""
553+
search_url = 'https://docs.aws.amazon.com/search?foo=bar'
554+
search_intent = 'test'
555+
result = add_search_intent_to_search_request(search_url, search_intent)
556+
# The function simply appends &search_intent=... to the URL
557+
assert result == 'https://docs.aws.amazon.com/search?foo=bar&search_intent=test'

0 commit comments

Comments
 (0)