{"id":7994,"date":"2026-05-26T15:05:04","date_gmt":"2026-05-26T21:05:04","guid":{"rendered":"https:\/\/gregsowell.com\/?p=7994"},"modified":"2026-05-26T15:05:30","modified_gmt":"2026-05-26T21:05:30","slug":"ansible-automation-platform-mcp-server-to-claude-code","status":"publish","type":"post","link":"https:\/\/gregsowell.com\/?p=7994","title":{"rendered":"Ansible Automation Platform MCP Server To Claude Code"},"content":{"rendered":"<p><a href=\"https:\/\/gregsowell.com\/wp-content\/uploads\/2026\/05\/ChatGPT-Image-May-26-2026-02_56_51-PM.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/gregsowell.com\/wp-content\/uploads\/2026\/05\/ChatGPT-Image-May-26-2026-02_56_51-PM-300x169.png\" alt=\"\" width=\"300\" height=\"169\" class=\"aligncenter size-medium wp-image-8007\" srcset=\"https:\/\/gregsowell.com\/wp-content\/uploads\/2026\/05\/ChatGPT-Image-May-26-2026-02_56_51-PM-300x169.png 300w, https:\/\/gregsowell.com\/wp-content\/uploads\/2026\/05\/ChatGPT-Image-May-26-2026-02_56_51-PM-768x432.png 768w, https:\/\/gregsowell.com\/wp-content\/uploads\/2026\/05\/ChatGPT-Image-May-26-2026-02_56_51-PM-1024x576.png 1024w, https:\/\/gregsowell.com\/wp-content\/uploads\/2026\/05\/ChatGPT-Image-May-26-2026-02_56_51-PM.png 1672w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><br \/>\nI&#8217;m using AI&#8230;you&#8217;re using AI&#8230;now how do we get it to play with the rest of our tools?  MCP is the way my friends.<\/p>\n<p>I&#8217;m going to cover how I quickly connected my AAP MCP to Claude Code(CC) running on a RHEL 9 VM.  I was initially trying to do it via the Windows desktop app, but as of this writing it&#8217;s not quite there yet.<\/p>\n<h2>Video Demo<\/h2>\n<p><iframe loading=\"lazy\" width=\"560\" height=\"315\" src=\"https:\/\/www.youtube.com\/embed\/0s2D5x3vXXE?si=e5JmyckqiLDJBpsd\" title=\"YouTube video player\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe><\/p>\n<h2>What is an MCP<\/h2>\n<p>First, an Model Context Protocol(MCP) is a standardized interface that exposes tools, data, and actions to an AI model, allowing it to interact with external systems in a structured, consistent way.<\/p>\n<p>Essentially an MCP acts as an interpreter for an LLM to talk to a software stack.<\/p>\n<p>Why would you need an MCP when your software already has an API?<br \/>\nEach API is a little bit different.  They have different options inside not to mention the way you interact with them is different for each.<br \/>\nAn MCP is always interfaced the same, so no matter if you are using ChatGPT, Claude, or any other Large Language Model(LLM), it will automatically know how to talk to it.  Not only this, but it can query the MCP to learn its capabilities, so the MCP will tell your LLM exactly what it can do!<\/p>\n<h2>Install The AAP MCP<\/h2>\n<p>Here&#8217;s the <a href=\"https:\/\/docs.redhat.com\/en\/documentation\/red_hat_ansible_automation_platform\/2.6\/extend-assembly_deploying_ansible_mcp_server#con-about-ansible-mcp-server___overview_of_deploying_the_ansible_mcp_server\">documentation on deploying your MCP on your AAP install<\/a>.  It&#8217;s really just a matter of grabbing a recent version of the installer, configuring the MCP section, and running said installer.<\/p>\n<p><strong>Configure inventory for installer<\/strong><\/p>\n<pre class=\"gs-code\"><code class=\"language-plaintext\"># This section is for your Ansible MCP Server host(s)\r\n# -----------------------------------------------------\r\n[ansiblemcp]\r\naap.example.com\r\n\r\n[all:vars]\r\nlightspeed_mcp_controller_enabled=true\r\nlightspeed_mcp_ignore_certificate_errors=true\r\nmcp_ignore_certificate_errors=true\r\nlightspeed_mcp_allow_write_operations=false\r\nmcp_allow_write_operations=false<\/code><\/pre>\n<p>Notice above that I turned off write permission, which means I will only be able to read from the MCP server, which means the LLM of choice can&#8217;t make any changes on my AAP instance.  I feel this is a good place to start.  Also note I have a mix of variable names for the same thing&#8230;a bit of old documentation mixed with new and I&#8217;m not sure which is the one to use moving forward(it&#8217;s not hurting to have them both for now LOL).<\/p>\n<p>Then rerun the installer and you should be good to go.<\/p>\n<pre class=\"gs-code\"><code class=\"language-plaintext\">ansible-playbook -i inventory.ini ansible.containerized_installer.install<\/code><\/pre>\n<p><strong>Note: <\/strong>The MCP server will default to port 8443, so ensure you have that open on your firewalls between your machine running Claude Code and the MCP server.<\/p>\n<h2>AAP API Token<\/h2>\n<p><a href=\"https:\/\/docs.redhat.com\/en\/documentation\/red_hat_ansible_automation_platform\/2.6\/extend-assembly_deploying_ansible_mcp_server#proc-create-api-token-ansible-mcp-server\">Documentation is here<\/a>.<br \/>\nTo authenticate your LLM to the MCP you have to setup a token.<br \/>\nGo to access management, users, select your user, click on the API Tokens tab, and click the + to add a new one:<br \/>\n<a href=\"https:\/\/gregsowell.com\/wp-content\/uploads\/2026\/05\/Screenshot_1.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/gregsowell.com\/wp-content\/uploads\/2026\/05\/Screenshot_1-300x153.png\" alt=\"\" width=\"300\" height=\"153\" class=\"aligncenter size-medium wp-image-7997\" srcset=\"https:\/\/gregsowell.com\/wp-content\/uploads\/2026\/05\/Screenshot_1-300x153.png 300w, https:\/\/gregsowell.com\/wp-content\/uploads\/2026\/05\/Screenshot_1-768x393.png 768w, https:\/\/gregsowell.com\/wp-content\/uploads\/2026\/05\/Screenshot_1.png 1005w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><br \/>\nHere you will give a description and set the scope.  So here I have the scope set to read, but even if I set it to write the LLM wouldn&#8217;t be able to write because when I installed the MCP server I disabled writing.  So setting this to read is kind of a belt and suspenders safety.  I want to be very specific with permissions, because at some point in the future I will set the MCP server to write, so I want to ensure users are correctly configured now.<\/p>\n<p>Once you click create token you will be supplied with the token one time, so be sure to save it in a password vault.<\/p>\n<p><strong>As a note<\/strong>, best practice is to have each user who wants to have their LLM connect to the MCP should use their own credential to create a token.  This means the MCP will only have access to what their credential has access to, so it helps prevent accidental access to unauthorized resources.  It also makes it easy to expire that user without affecting anyone else.<\/p>\n<h2>Configure Claude Code<\/h2>\n<p>From the CC project folder where you want to access your LLM, issue the following commands.  Note that at the time of this writing these are all of the supported MCP servers for AAP.  Each one supplies access to different aspects of AAP and each has its own capabilities.  Only enter the ones you want your LLM to access:<\/p>\n<pre class=\"gs-code\"><code class=\"language-plaintext\">claude mcp add aap-job --transport http &quot;https:\/\/aap.example.com:8448\/job_management\/mcp&quot; --header &quot;Authorization: Bearer YourTokenHere&quot;\r\nclaude mcp add aap-inv --transport http &quot;https:\/\/aap.example.com:8448\/inventory_management\/mcp&quot; --header &quot;Authorization: Bearer YourTokenHere&quot;\r\nclaude mcp add aap-mon --transport http &quot;https:\/\/aap.example.com:8448\/system_monitoring\/mcp&quot; --header &quot;Authorization: Bearer YourTokenHere&quot;\r\nclaude mcp add aap-user --transport http &quot;https:\/\/aap.example.com:8448\/user_management\/mcp&quot; --header &quot;Authorization: Bearer YourTokenHere&quot;\r\nclaude mcp add aap-sec --transport http &quot;https:\/\/aap.example.com:8448\/security_compliance\/mcp&quot; --header &quot;Authorization: Bearer YourTokenHere&quot;<\/code><\/pre>\n<p>Here a breakdown of what each does:<br \/>\n<strong>Job management<\/strong><br \/>\nTools to list available job templates, launch automation jobs, and monitor their real-time status.<\/p>\n<p><strong>Inventory management<\/strong><br \/>\nTools to query your inventory for host details, check group membership, and verify system facts.<\/p>\n<p><strong>System monitoring<\/strong><br \/>\nTools to retrieve job logs, troubleshoot failed tasks, and check the health of your automation environment.<\/p>\n<p><strong>User management<\/strong><br \/>\nTools to allow the AI agent to administer access and organizational structure within Ansible Automation Platform.<\/p>\n<p><strong>Security and compliance<\/strong><br \/>\nTools that enable the AI agent to act as a security operator, managing sensitive credentials and verifying platform integrity without exposing raw secrets. <\/p>\n<p><strong>Platform configuration<\/strong><br \/>\nTools that enable administrators and developers to inspect and tune Ansible Automation Platform infrastructure itself. <\/p>\n<p>Now when you load up claude from that folder you will be able to access these MCPs natively.<\/p>\n<p>To view their status issue(optional):<\/p>\n<pre class=\"gs-code\"><code class=\"language-plaintext\">\/mcp<\/code><\/pre>\n<p>A decent prompt to start with is:<\/p>\n<pre class=\"gs-code\"><code class=\"language-plaintext\">Using the aap mcp tools, XXX<\/code><\/pre>\n<p>So this could be something like:<\/p>\n<pre class=\"gs-code\"><code class=\"language-plaintext\">Using the aap mcp tools, find the last job templates that ran against the nexus9k3 switch and let me know the name of the job along with its outcome.\r\n\r\nHere&#039;s the full report of every job where nexus9k3 was involved across your AAP environment:\r\n\r\n  ---\r\n  nexus9k3 Job Report\r\n\r\n  Total jobs scanned: 185\r\n  Jobs involving nexus9k3: 73 jobs across 5 job templates\r\n\r\n  ---\r\n  1. group_vars testing \u2014 1 job\r\n\r\n  \u250c\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\r\n  \u2502 Job \u2502         Status          \u2502           Result for nexus9k3            \u2502\r\n  \u251c\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\r\n  \u2502 500 \u2502 Successful (2026-01-26) \u2502 OK: 1, Changed: 0, Failed: 0, Skipped: 0 \u2502\r\n  \u2514\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\r\n\r\n  ---\r\n  2. Netbox - nexus vlans \u2014 21 jobs\r\n\r\n  - All successful except job 1030 (2026-04-30), where nexus9k3 had 1 failed task\r\n  - Typical result: OK: 2, Changed: 0\u20132, Skipped: 2\u20134\r\n  - One run (job 535) had nexus9k3 fully skipped (all 4 tasks skipped)\r\n\r\n  ---\r\n  3. Netbox - nexus int desc \u2014 16 jobs\r\n\r\n  - All successful, all runs clean\r\n  - Typical result: OK: 1, Changed: 0\u20131, Skipped: 0\r\n\r\n  ---\r\n  4. nexus stale interface report \u2014 33 jobs (2026-02-27 to 2026-02-28)\r\n\r\n  - 7 jobs where nexus9k3 had failed tasks: Jobs 791, 811, 836, 842, 846, 852, 854\r\n  - Failures showed OK: 0\u20132, Failed: 1, suggesting intermittent connectivity or task issues during that testing period\r\n  - All other runs in this template were successful for nexus9k3\r\n\r\n  ---\r\n  5. python networking integration \u2014 16 jobs (2026-03-05)\r\n\r\n  - 2 early runs failed for nexus9k3: Jobs 869, 871 (OK: 0, Failed: 1)\r\n  - Template was stabilized after those initial failures\r\n  - All subsequent runs were successful\r\n\r\n  ---\r\n  Key Takeaways\r\n\r\n  - nexus9k3 was never unreachable in any job (unreachable count always 0)\r\n  - The bulk of failures were concentrated in late Feb 2026 during &quot;nexus stale interface report&quot; development and early March 2026\r\n  during &quot;python networking integration&quot; testing \u2014 both appear to be transient issues during template development\r\n  - The Netbox vlan\/int desc jobs have been rock solid for nexus9k3, with only a single task failure in job 1030 on 2026-04-30<\/code><\/pre>\n<h2>Connecting your LLM With Self Signed Certs<\/h2>\n<p>In my lab I went direct to the server rather than through the normal channels so it couldn&#8217;t authenticate the cert.  What I did was connect to the VM with the AAP container and nabbed the root CA files and installed them on my RHEL server running CC.  You could follow these steps on your own or have CC walk you through the process LOL.<\/p>\n<p><strong>1. Diagnosing<\/strong> that your failure to connect is the cert:<\/p>\n<pre class=\"gs-code\"><code class=\"language-plaintext\">curl -v https:\/\/aap.example.com:8448\/job_management\/mcp -H &quot;Authorization: &lt;token&gt;&quot;<\/code><\/pre>\n<p>Look for: SSL certificate problem: unable to get local issuer certificate \u2014 confirms a CA trust issue.<\/p>\n<p><strong>2. Find <\/strong>the CA Cert on the AAP Server<\/p>\n<p>SSH into the AAP host and locate the CA cert:<\/p>\n<pre class=\"gs-code\"><code class=\"language-plaintext\">find \/home \/root \/opt \/var -name &quot;*.crt&quot; -o -name &quot;*.pem&quot; 2&gt;\/dev\/null | grep -iv &quot;mozilla\\|nss\\|pki\/tls&quot;<\/code><\/pre>\n<p>For containerized AAP 2.6 on RHEL 9, it&#8217;s typically at:<\/p>\n<pre class=\"gs-code\"><code class=\"language-plaintext\">\/home\/&lt;user&gt;\/.config\/containers\/certs.d\/aap.example.com:443\/ca.crt<\/code><\/pre>\n<p>Verify it&#8217;s the right cert (subject and issuer should both say Ansible Automation Platform):<\/p>\n<pre class=\"gs-code\"><code class=\"language-plaintext\">openssl x509 -in \/home\/gsowell\/.config\/containers\/certs.d\/aap.example.com:443\/ca.crt -noout -subject -issuer<\/code><\/pre>\n<p><strong>3. Copy <\/strong>and Extract the CA Cert<\/p>\n<p>Copy from the AAP host to the machine running Claude Code:<\/p>\n<pre class=\"gs-code\"><code class=\"language-plaintext\">scp gsowell@aap.example.com:\/home\/gsowell\/.config\/containers\/certs.d\/aap.example.com\\:443\/ca.crt \/tmp\/aap-root-ca.crt<\/code><\/pre>\n<p>Extract just the AAP CA cert from the bundle:<\/p>\n<pre class=\"gs-code\"><code class=\"language-plaintext\">  csplit -z -f \/tmp\/aap-cert- \/tmp\/aap-root-ca.crt &#039;\/-----BEGIN CERTIFICATE-----\/&#039; &#039;{*}&#039;\r\n  for f in \/tmp\/aap-cert-*; do\r\n    subj=$(openssl x509 -in &quot;$f&quot; -noout -subject 2&gt;\/dev\/null)\r\n    if echo &quot;$subj&quot; | grep -q &quot;Ansible Automation Platform&quot;; then\r\n      cp &quot;$f&quot; \/tmp\/aap-ca-only.crt\r\n      echo &quot;Extracted: $f&quot;\r\n    fi\r\n  done<\/code><\/pre>\n<p>  Verify the extracted cert:<\/p>\n<pre class=\"gs-code\"><code class=\"language-plaintext\">  openssl x509 -in \/tmp\/aap-ca-only.crt -noout -subject -issuer<\/code><\/pre>\n<p><strong>4. Add<\/strong> to System Trust Store<\/p>\n<pre class=\"gs-code\"><code class=\"language-plaintext\">  sudo cp \/tmp\/aap-ca-only.crt \/etc\/pki\/ca-trust\/source\/anchors\/aap-root-ca.crt\r\n  sudo update-ca-trust<\/code><\/pre>\n<p><strong>5. Verify<\/strong><\/p>\n<pre class=\"gs-code\"><code class=\"language-plaintext\">  curl -s https:\/\/aap.example.com:8448\/job_management\/mcp \\\r\n    -H &quot;Authorization: &lt;token&gt;&quot; -o \/dev\/null -w &quot;HTTP status: %{http_code}\\n&quot;<\/code><\/pre>\n<p>Anything other than a TLS error (e.g. 200, 405) confirms the cert is trusted.<\/p>\n<h2>Conclusion<\/h2>\n<p>Connecting to the AAP MCP isn&#8217;t too bad.  I look forward to the standard desktop client getting access, but until then I plan to build some interesting workflows.<br \/>\nHow do you plan to use this&#8230;have anything interesting planned?<\/p>\n<p>Thanks for reading and happy MCPing \ud83d\ude42<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I&#8217;m using AI&#8230;you&#8217;re using AI&#8230;now how do we get it to play with the rest of our tools? MCP is the way my friends.\u2026<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[49],"tags":[],"class_list":["post-7994","post","type-post","status-publish","format-standard","hentry","category-ansible"],"_links":{"self":[{"href":"https:\/\/gregsowell.com\/index.php?rest_route=\/wp\/v2\/posts\/7994","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/gregsowell.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/gregsowell.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/gregsowell.com\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/gregsowell.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=7994"}],"version-history":[{"count":9,"href":"https:\/\/gregsowell.com\/index.php?rest_route=\/wp\/v2\/posts\/7994\/revisions"}],"predecessor-version":[{"id":8008,"href":"https:\/\/gregsowell.com\/index.php?rest_route=\/wp\/v2\/posts\/7994\/revisions\/8008"}],"wp:attachment":[{"href":"https:\/\/gregsowell.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=7994"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/gregsowell.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=7994"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/gregsowell.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=7994"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}