infrawise 0.7.0 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,6 +4,7 @@
4
4
  [![Publish to npm](https://github.com/Sidd27/infrawise/actions/workflows/npm-publish.yml/badge.svg)](https://github.com/Sidd27/infrawise/actions/workflows/npm-publish.yml)
5
5
  [![CI](https://github.com/Sidd27/infrawise/actions/workflows/ci.yml/badge.svg)](https://github.com/Sidd27/infrawise/actions/workflows/ci.yml)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
7
+ [![infrawise MCP server](https://glama.ai/mcp/servers/Sidd27/infrawise/badges/score.svg)](https://glama.ai/mcp/servers/Sidd27/infrawise)
7
8
 
8
9
  **Understand your infrastructure, not just your code.**
9
10
 
@@ -22,12 +23,14 @@ AI coding assistants can read your source files but have no deterministic knowle
22
23
  Infrawise replaces guessing with infrastructure-aware context.
23
24
 
24
25
  **Without Infrawise**, an AI assistant might:
26
+
25
27
  - Suggest a `.scan()` on your Orders table that has 50M rows
26
28
  - Recommend adding a GSI on `status` that you already have
27
29
  - Write a `SELECT *` when you need to keep query cost low
28
30
  - Not notice that 5 functions are already hammering the same partition key
29
31
 
30
32
  **With Infrawise**, it knows:
33
+
31
34
  - Your exact table schemas, partition keys, sort keys, and GSIs
32
35
  - Which functions query which tables and how
33
36
  - Which patterns are already flagged as high severity
@@ -157,34 +160,56 @@ To let Claude Code manage the server lifecycle automatically:
157
160
 
158
161
  ### MCP tools
159
162
 
160
- | Tool | What it provides |
161
- |---|---|
162
- | `get_infra_overview` | Complete snapshot — all services, counts, and high-severity findings |
163
- | `get_graph_summary` | Full infrastructure graph — all nodes, edges, and findings |
164
- | `analyze_function` | Issues in a specific function — scans, missing indexes, N+1, trigger event shapes |
165
- | `suggest_gsi` | Exact GSI config for a DynamoDB table + attribute |
166
- | `postgres_index_suggestions` | Exact `CREATE INDEX` SQL for your actual table |
167
- | `suggest_mongo_index` | Exact `createIndex` command for a MongoDB collection + field |
168
- | `mysql_index_suggestions` | Exact `ALTER TABLE ADD INDEX` SQL for your MySQL table |
169
- | `get_queue_details` | SQS queues — DLQ status, encryption, message counts |
170
- | `get_topic_details` | SNS topics — subscription counts and protocols |
171
- | `get_secrets_overview` | Secrets Manager — names and rotation status (values never included) |
172
- | `get_parameter_overview` | SSM Parameter Store — names, types, tiers (values never included) |
173
- | `get_lambda_overview` | Lambda functions — runtime, memory, timeout, triggers (SQS/DynamoDB/Kinesis/EventBridge), env var key names |
174
- | `get_eventbridge_details` | EventBridge rules — name, state, schedule/event pattern, target functions |
175
- | `get_log_errors` | CloudWatch error patterns and counts (no raw log messages) |
163
+ | Tool | What it provides |
164
+ | ---------------------------- | ----------------------------------------------------------------------------------------------------------- |
165
+ | `get_infra_overview` | Complete snapshot — all services, counts, and high-severity findings |
166
+ | `get_graph_summary` | Full infrastructure graph — all nodes, edges, and findings |
167
+ | `analyze_function` | Issues in a specific function — scans, missing indexes, N+1, trigger event shapes |
168
+ | `suggest_gsi` | Exact GSI config for a DynamoDB table + attribute |
169
+ | `postgres_index_suggestions` | Exact `CREATE INDEX` SQL for your actual table |
170
+ | `suggest_mongo_index` | Exact `createIndex` command for a MongoDB collection + field |
171
+ | `mysql_index_suggestions` | Exact `ALTER TABLE ADD INDEX` SQL for your MySQL table |
172
+ | `get_queue_details` | SQS queues — DLQ status, encryption, message counts |
173
+ | `get_topic_details` | SNS topics — subscription counts and protocols |
174
+ | `get_secrets_overview` | Secrets Manager — names and rotation status (values never included) |
175
+ | `get_parameter_overview` | SSM Parameter Store — names, types, tiers (values never included) |
176
+ | `get_lambda_overview` | Lambda functions — runtime, memory, timeout, triggers (SQS/DynamoDB/Kinesis/EventBridge), env var key names |
177
+ | `get_eventbridge_details` | EventBridge rules — name, state, schedule/event pattern, target functions |
178
+ | `get_log_errors` | CloudWatch error patterns and counts (no raw log messages) |
176
179
 
177
180
  ---
178
181
 
179
182
  ## CLI reference
180
183
 
181
- | Command | What it does |
182
- |---|---|
183
- | `infrawise init` | Detect AWS + repo, generate `infrawise.yaml` |
184
- | `infrawise auth` | Select or switch AWS profile |
185
- | `infrawise analyze` | Scan repo + AWS, build graph, print findings |
186
- | `infrawise dev` | Start MCP server — auto-analyzes if no cache, watches files for live refresh |
187
- | `infrawise doctor` | Validate AWS access, DB connectivity, and config |
184
+ | Command | What it does |
185
+ | ------------------- | ---------------------------------------------------------------------------- |
186
+ | `infrawise init` | Detect AWS + repo, generate `infrawise.yaml` |
187
+ | `infrawise auth` | Select or switch AWS profile |
188
+ | `infrawise analyze` | Scan repo + AWS, build graph, print findings |
189
+ | `infrawise dev` | Start MCP server — auto-analyzes if no cache, watches files for live refresh |
190
+ | `infrawise stdio` | Start MCP server on stdio transport (for Claude Desktop) |
191
+ | `infrawise doctor` | Validate AWS access, DB connectivity, and config |
192
+
193
+ ### `infrawise analyze` options
194
+
195
+ | Flag | Description |
196
+ | --------------------- | ---------------------------------------------------------------------- |
197
+ | `-c, --config <path>` | Path to `infrawise.yaml` (default: `infrawise.yaml`) |
198
+ | `-r, --repo <path>` | Repository to scan (default: current directory) |
199
+ | `--no-cache` | Skip reading/writing the cache |
200
+ | `-o, --output <path>` | Save findings as a markdown report, e.g. `report.md` |
201
+ | `--severity <level>` | Only show findings at or above this level: `high` \| `medium` \| `low` |
202
+
203
+ ```bash
204
+ # Export a shareable findings report
205
+ infrawise analyze --output report.md
206
+
207
+ # Only show high-severity issues
208
+ infrawise analyze --severity high
209
+
210
+ # High-severity issues only, saved to a file
211
+ infrawise analyze --severity high --output report.md
212
+ ```
188
213
 
189
214
  ---
190
215
 
@@ -206,12 +231,12 @@ Full example:
206
231
  project: payments-service
207
232
 
208
233
  aws:
209
- profile: default # AWS profile from ~/.aws/credentials
234
+ profile: default # AWS profile from ~/.aws/credentials
210
235
  region: ap-south-1
211
236
 
212
237
  dynamodb:
213
238
  enabled: true
214
- includeTables: # omit to include all tables
239
+ includeTables: # omit to include all tables
215
240
  - Orders
216
241
  - Users
217
242
 
@@ -221,11 +246,11 @@ postgres:
221
246
 
222
247
  mysql:
223
248
  enabled: false
224
- connectionString: ""
249
+ connectionString: ''
225
250
 
226
251
  mongodb:
227
252
  enabled: false
228
- connectionString: ""
253
+ connectionString: ''
229
254
 
230
255
  sqs:
231
256
  enabled: true
@@ -235,16 +260,16 @@ sns:
235
260
 
236
261
  ssm:
237
262
  enabled: true
238
- paths: [] # filter by prefix e.g. ["/myapp/prod"]
263
+ paths: [] # filter by prefix e.g. ["/myapp/prod"]
239
264
 
240
265
  secretsManager:
241
266
  enabled: true
242
267
 
243
268
  lambda:
244
269
  enabled: true
245
- includeFunctions: # omit to include all functions
246
- - processOrders
247
- - generateReport
270
+ includeFunctions: # omit to include all functions
271
+ - myFunction
272
+ - anotherFunction
248
273
 
249
274
  eventbridge:
250
275
  enabled: true
@@ -274,10 +299,7 @@ Infrawise is **read-only**. Minimum IAM policy required:
274
299
  "Statement": [
275
300
  {
276
301
  "Effect": "Allow",
277
- "Action": [
278
- "dynamodb:ListTables",
279
- "dynamodb:DescribeTable"
280
- ],
302
+ "Action": ["dynamodb:ListTables", "dynamodb:DescribeTable"],
281
303
  "Resource": "*"
282
304
  }
283
305
  ]
@@ -313,36 +335,36 @@ Infrawise has two analysis layers:
313
335
 
314
336
  Works from AWS APIs, database schema introspection, and IaC files — no dependency on application code:
315
337
 
316
- | Service | What it checks |
317
- |---|---|
318
- | DynamoDB schema | Tables, GSIs, partition keys |
319
- | PostgreSQL / MySQL schema | Tables, indexes, column types |
320
- | MongoDB schema | Collections, indexes |
321
- | SQS | Missing DLQs, unencrypted queues, large backlogs |
322
- | Kafka (kafkajs) | Producer/consumer topic mapping from code |
323
- | Secrets Manager | Missing secret rotation |
324
- | Lambda | Default memory (128 MB), high timeouts, triggers (SQS/DynamoDB/Kinesis/EventBridge), missing DLQ on trigger source |
325
- | EventBridge | Rules, schedules, event patterns, target Lambda functions |
326
- | RDS | Publicly accessible, no backups, unencrypted, no deletion protection, single-AZ |
327
- | CloudWatch Logs | Log groups with no retention policy |
328
- | Terraform / CloudFormation / CDK | IaC drift vs deployed state |
338
+ | Service | What it checks |
339
+ | -------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
340
+ | DynamoDB schema | Tables, GSIs, partition keys |
341
+ | PostgreSQL / MySQL schema | Tables, indexes, column types |
342
+ | MongoDB schema | Collections, indexes |
343
+ | SQS | Missing DLQs, unencrypted queues, large backlogs |
344
+ | Kafka (kafkajs) | Producer/consumer topic mapping from code |
345
+ | Secrets Manager | Missing secret rotation |
346
+ | Lambda | Default memory (128 MB), high timeouts, triggers (SQS/DynamoDB/Kinesis/EventBridge), missing DLQ on trigger source |
347
+ | EventBridge | Rules, schedules, event patterns, target Lambda functions |
348
+ | RDS | Publicly accessible, no backups, unencrypted, no deletion protection, single-AZ |
349
+ | CloudWatch Logs | Log groups with no retention policy |
350
+ | Terraform / CloudFormation / CDK | IaC drift vs deployed state |
329
351
 
330
352
  ### Code correlation analysis (TypeScript / JavaScript)
331
353
 
332
354
  Uses [ts-morph](https://ts-morph.com/) AST analysis to detect which functions call which tables and how:
333
355
 
334
- | Analyzer | Severity | What it detects |
335
- |---|---|---|
336
- | Full Table Scan (DynamoDB) | High | `.scan()` calls without filters |
337
- | Missing GSI | Medium | Queries on attributes without a matching GSI |
338
- | Hot Partition | Medium | 5+ distinct code paths hitting the same table |
339
- | Missing Index (PostgreSQL) | Medium | Tables queried without indexes |
340
- | N+1 Query | Medium | Repeated query patterns from ORM loops |
341
- | Large SELECT | Low | `SELECT *` usage |
342
- | Missing MySQL Index | Medium | MySQL tables queried without indexes |
343
- | MySQL Full Table Scan | High | Full table scan patterns in MySQL queries |
344
- | Missing Mongo Index | Medium | Collections queried without secondary indexes |
345
- | Collection Scan | High | `find()` calls without filter predicates |
356
+ | Analyzer | Severity | What it detects |
357
+ | -------------------------- | -------- | --------------------------------------------- |
358
+ | Full Table Scan (DynamoDB) | High | `.scan()` calls without filters |
359
+ | Missing GSI | Medium | Queries on attributes without a matching GSI |
360
+ | Hot Partition | Medium | 5+ distinct code paths hitting the same table |
361
+ | Missing Index (PostgreSQL) | Medium | Tables queried without indexes |
362
+ | N+1 Query | Medium | Repeated query patterns from ORM loops |
363
+ | Large SELECT | Low | `SELECT *` usage |
364
+ | Missing MySQL Index | Medium | MySQL tables queried without indexes |
365
+ | MySQL Full Table Scan | High | Full table scan patterns in MySQL queries |
366
+ | Missing Mongo Index | Medium | Collections queried without secondary indexes |
367
+ | Collection Scan | High | `find()` calls without filter predicates |
346
368
 
347
369
  Non-TypeScript/JavaScript projects still get full value from infrastructure-level analyzers — code correlation (function-to-table mapping, N+1 patterns) is skipped.
348
370
 
@@ -377,29 +399,52 @@ Infrawise does not use an LLM to analyze your infrastructure. All extraction and
377
399
 
378
400
  ## Architecture overview
379
401
 
380
- ```
381
- Your repo (any language) Your repo (TS/JS only)
382
- │ │
383
- │ Repository Scanner (ts-morph AST)
384
- │ which functions → which tables
385
- │ │
386
- ┌───────┴──────────────────────────────────┴────────────┐
387
- │ infrawise analyze │
388
- │ │
389
- │ AWS APIs / DB schema / IaC files + Code ops (opt) │
390
- │ (works for any project) (TS/JS only) │
391
- │ │ │
392
- │ Graph Engine │
393
- │ (nodes + edges) │
394
- │ │ │
395
- │ Analyzer Engine │
396
- │ (rule-based, deterministic) │
397
- └─────────────────────────┬─────────────────────────────┘
398
-
399
- ┌──────────────────┐
400
- │ MCP Server │ ◄── Claude Code
401
- │ localhost:3000 │ ◄── Cursor
402
- └──────────────────┘ ◄── Windsurf
402
+ ```mermaid
403
+ flowchart LR
404
+ subgraph IN["Your Infrastructure & Code"]
405
+ direction TB
406
+ D["DynamoDB"]
407
+ L["Lambda · SQS · SNS\nEventBridge · RDS"]
408
+ S["Secrets Manager · SSM\nCloudWatch"]
409
+ P["PostgreSQL · MySQL"]
410
+ M["MongoDB"]
411
+ T["Terraform · CDK\nCloudFormation"]
412
+ C["TypeScript / JS"]
413
+ end
414
+
415
+ A["Adapters"]
416
+ G["Graph Engine"]
417
+ AN["23 Analyzers"]
418
+ CA["Cache"]
419
+
420
+ subgraph SV["infrawise dev"]
421
+ MCP["MCP Server\nlocalhost:3000/mcp"]
422
+ end
423
+
424
+ subgraph AI["AI Coding Assistants"]
425
+ direction TB
426
+ CC["Claude Code"]
427
+ CU["Cursor"]
428
+ WS["Windsurf"]
429
+ end
430
+
431
+ D & L & S & P & M & T & C --> A
432
+ A --> G --> AN --> CA --> MCP
433
+ MCP --> CC & CU & WS
434
+
435
+ classDef aws fill:#FF9900,stroke:#232F3E,color:#000
436
+ classDef db fill:#336791,stroke:#1a3a5c,color:#fff
437
+ classDef iac fill:#7B42BC,stroke:#4a2080,color:#fff
438
+ classDef code fill:#3178C6,stroke:#1a4a80,color:#fff
439
+ classDef iw fill:#1a1a2e,stroke:#e94560,color:#fff
440
+ classDef ai fill:#10a37f,stroke:#0a6b54,color:#fff
441
+
442
+ class D,L,S aws
443
+ class P,M db
444
+ class T iac
445
+ class C code
446
+ class A,G,AN,CA,MCP iw
447
+ class CC,CU,WS ai
403
448
  ```
404
449
 
405
450
  ### Source layout
@@ -409,8 +454,10 @@ src/
409
454
  types.ts Shared type definitions
410
455
  core/ Config (Zod + YAML), logger (Pino), local cache
411
456
  graph/ Graph engine — nodes, edges, builder
412
- adapters/ Flat extractors: dynamodb.ts, postgres.ts, mysql.ts,
413
- mongodb.ts, aws.ts, logs.ts, terraform.ts
457
+ adapters/
458
+ aws/ DynamoDB, Lambda, SQS/SNS/SSM/Secrets/EventBridge/RDS, CloudWatch
459
+ db/ PostgreSQL, MySQL, MongoDB
460
+ iac/ Terraform, CDK, CloudFormation (local file parsing)
414
461
  analyzers/ 23 rule-based analyzers
415
462
  context/ Repository scanner (ts-morph AST)
416
463
  server/ Fastify MCP server (@modelcontextprotocol/sdk, Streamable HTTP)
@@ -430,20 +477,7 @@ src/
430
477
 
431
478
  ## Roadmap
432
479
 
433
- Feature roadmap is tracked in the [GitHub Project](https://github.com/users/Sidd27/projects/1). Priorities, complexity, and virality scores are visible there. Feature requests and upvotes welcome.
434
-
435
- ### Planned
436
- - Runtime tracing integration
437
- - Incremental analysis for large monorepos
438
- - Kubernetes workload graphing
439
- - VS Code extension
440
- - Infrastructure drift detection
441
- - MSK (Amazon Managed Streaming for Apache Kafka) — cluster metadata + topic listing via MSK API and Kafka admin client
442
-
443
- ### Under consideration
444
- - OpenTelemetry integration
445
- - CI/CD reporting mode
446
- - Multi-repository graph correlation
480
+ Feature roadmap is tracked in the [GitHub Project](https://github.com/users/Sidd27/projects/1). Feature requests and upvotes welcome.
447
481
 
448
482
  ---
449
483
 
@@ -455,47 +489,7 @@ The `demo/localstack/` directory runs infrawise against real AWS APIs emulated l
455
489
 
456
490
  ## Contributing
457
491
 
458
- ### Prerequisites
459
-
460
- Node.js 24+, pnpm 9+, AWS CLI (for integration testing).
461
-
462
- ### Setup
463
-
464
- ```bash
465
- git clone https://github.com/Sidd27/infrawise
466
- cd infrawise
467
- pnpm install
468
- pnpm build
469
- ```
470
-
471
- ### Development workflow
472
-
473
- ```bash
474
- pnpm build # compile
475
- pnpm test # run all tests
476
- pnpm typecheck # TypeScript strict check
477
- pnpm lint # ESLint
478
- ```
479
-
480
- ### Adding a new analyzer
481
-
482
- 1. Create your analyzer in `src/analyzers/`
483
- 2. Implement the `Analyzer` interface:
484
- ```ts
485
- export class MyAnalyzer implements Analyzer {
486
- name = 'MyAnalyzer';
487
- async analyze(graph: SystemGraph): Promise<Finding[]> { ... }
488
- }
489
- ```
490
- 3. Export it from `src/analyzers/index.ts`
491
- 4. Add tests in `src/analyzers/__tests__/`
492
-
493
- ### Adding a new database adapter
494
-
495
- 1. Create your extractor as `src/adapters/yourdb.ts`
496
- 2. Export a function returning `Promise<YourTableMetadata[]>`
497
- 3. Add the metadata type to `src/types.ts` if needed
498
- 4. Wire it into `src/cli/commands/analyze.ts`
492
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for a full walkthrough — including how to add a new service adapter, a new analyzer, and the PR checklist.
499
493
 
500
494
  ### Releasing
501
495
 
@@ -508,14 +502,6 @@ pnpm release 1.5.0 # explicit version
508
502
 
509
503
  Bumps `package.json`, commits, tags, pushes, and creates a draft GitHub release with notes from commit messages. Then publish the draft on GitHub to trigger npm publish.
510
504
 
511
- ### PR checklist
512
-
513
- - `pnpm lint` passes
514
- - `pnpm typecheck` passes
515
- - `pnpm test` passes
516
- - New analyzers have unit tests with mock graph data
517
- - No hardcoded AWS regions or credentials
518
-
519
505
  ---
520
506
 
521
507
  ## License
@@ -1,6 +1,6 @@
1
1
  import { DynamoDBClient, ListTablesCommand, DescribeTableCommand, } from '@aws-sdk/client-dynamodb';
2
2
  import { fromIni } from '@aws-sdk/credential-providers';
3
- import { DynamoDBError, logger } from '../core/index.js';
3
+ import { DynamoDBError, logger } from '../../core/index.js';
4
4
  function createDynamoClient(config) {
5
5
  const region = config.aws?.region ?? 'us-east-1';
6
6
  const profile = config.aws?.profile;
@@ -0,0 +1,3 @@
1
+ export * from './services.js';
2
+ export * from './dynamodb.js';
3
+ export * from './logs.js';
@@ -1,6 +1,6 @@
1
1
  import { CloudWatchLogsClient, DescribeLogGroupsCommand, FilterLogEventsCommand, } from '@aws-sdk/client-cloudwatch-logs';
2
2
  import { fromIni } from '@aws-sdk/credential-providers';
3
- import { logger } from '../core/index.js';
3
+ import { logger } from '../../core/index.js';
4
4
  // Hard caps to prevent context bloat
5
5
  const MAX_LOG_GROUPS = 50;
6
6
  const MAX_EVENTS_PER_GROUP = 50;
@@ -6,7 +6,7 @@ import { LambdaClient, ListFunctionsCommand, ListEventSourceMappingsCommand, } f
6
6
  import { EventBridgeClient, ListRulesCommand, ListTargetsByRuleCommand, } from '@aws-sdk/client-eventbridge';
7
7
  import { RDSClient, DescribeDBInstancesCommand, } from '@aws-sdk/client-rds';
8
8
  import { fromIni } from '@aws-sdk/credential-providers';
9
- import { logger } from '../core/index.js';
9
+ import { logger } from '../../core/index.js';
10
10
  function clientConfig(cfg) {
11
11
  const region = cfg.region ?? 'us-east-1';
12
12
  const base = { region };
@@ -0,0 +1,3 @@
1
+ export * from './postgres.js';
2
+ export * from './mysql.js';
3
+ export * from './mongodb.js';
@@ -1,5 +1,5 @@
1
1
  import { MongoClient } from 'mongodb';
2
- import { InfrawiseError, logger } from '../core/index.js';
2
+ import { InfrawiseError, logger } from '../../core/index.js';
3
3
  const SYSTEM_DATABASES = new Set(['admin', 'local', 'config']);
4
4
  export class MongoConnectionError extends InfrawiseError {
5
5
  constructor(details) {
@@ -1,5 +1,5 @@
1
1
  import mysql from 'mysql2/promise';
2
- import { InfrawiseError, logger } from '../core/index.js';
2
+ import { InfrawiseError, logger } from '../../core/index.js';
3
3
  const SYSTEM_SCHEMAS = new Set(['information_schema', 'performance_schema', 'mysql', 'sys']);
4
4
  export class MySQLConnectionError extends InfrawiseError {
5
5
  constructor(details) {
@@ -1,5 +1,5 @@
1
1
  import { Pool } from 'pg';
2
- import { PostgresConnectionError, logger } from '../core/index.js';
2
+ import { PostgresConnectionError, logger } from '../../core/index.js';
3
3
  export async function extractPostgresMetadata(connectionString) {
4
4
  const pool = new Pool({
5
5
  connectionString,
@@ -0,0 +1 @@
1
+ export * from './terraform.js';
@@ -1,7 +1,7 @@
1
1
  import * as fs from 'fs';
2
2
  import * as path from 'path';
3
3
  import yaml from 'js-yaml';
4
- import { logger } from '../core/index.js';
4
+ import { logger } from '../../core/index.js';
5
5
  function emptySchema() {
6
6
  return {
7
7
  dynamoTables: [],
@@ -1,18 +1,43 @@
1
+ import * as fs from 'fs';
1
2
  import * as path from 'path';
2
3
  import chalk from 'chalk';
3
4
  import ora from 'ora';
4
5
  import { loadConfig, formatError, writeCache, readCache } from '../../core/index.js';
5
- import { extractDynamoMetadata } from '../../adapters/dynamodb.js';
6
- import { extractPostgresMetadata } from '../../adapters/postgres.js';
7
- import { extractMySQLMetadata } from '../../adapters/mysql.js';
8
- import { extractMongoMetadata } from '../../adapters/mongodb.js';
9
- import { extractIaCSchema } from '../../adapters/terraform.js';
10
- import { extractSQSMetadata, extractSNSMetadata, extractSSMMetadata, extractSecretsMetadata, extractLambdaMetadata, extractEventBridgeMetadata, extractRDSMetadata, } from '../../adapters/aws.js';
11
- import { extractLogsMetadata } from '../../adapters/logs.js';
6
+ import { extractDynamoMetadata } from '../../adapters/aws/dynamodb.js';
7
+ import { extractPostgresMetadata } from '../../adapters/db/postgres.js';
8
+ import { extractMySQLMetadata } from '../../adapters/db/mysql.js';
9
+ import { extractMongoMetadata } from '../../adapters/db/mongodb.js';
10
+ import { extractIaCSchema } from '../../adapters/iac/terraform.js';
11
+ import { extractSQSMetadata, extractSNSMetadata, extractSSMMetadata, extractSecretsMetadata, extractLambdaMetadata, extractEventBridgeMetadata, extractRDSMetadata, } from '../../adapters/aws/services.js';
12
+ import { extractLogsMetadata } from '../../adapters/aws/logs.js';
12
13
  import { scanRepository } from '../../context/index.js';
13
14
  import { buildGraph } from '../../graph/index.js';
14
15
  import { runAllAnalyzers, IaCDriftAnalyzer, FullTableScanAnalyzer, MissingGSIAnalyzer, HotPartitionAnalyzer, MissingIndexAnalyzer, NplusOneAnalyzer, LargeSelectAnalyzer, MissingMySQLIndexAnalyzer, MySQLFullTableScanAnalyzer, MissingMongoIndexAnalyzer, MongoCollectionScanAnalyzer, MissingDLQAnalyzer, UnencryptedQueueAnalyzer, LargeQueueBacklogAnalyzer, MissingSecretRotationAnalyzer, MissingLogRetentionAnalyzer, LambdaDefaultMemoryAnalyzer, LambdaHighTimeoutAnalyzer, LambdaMissingTriggerDLQAnalyzer, RDSPubliclyAccessibleAnalyzer, RDSNoBackupAnalyzer, RDSUnencryptedAnalyzer, RDSNoDeletionProtectionAnalyzer, RDSNoMultiAZAnalyzer, } from '../../analyzers/index.js';
15
16
  import { printFinding, printSummaryBox, log, printHeader } from '../utils.js';
17
+ const SEVERITY_ORDER = { high: 3, medium: 2, low: 1 };
18
+ function buildMarkdownReport(findings, projectName) {
19
+ const date = new Date().toISOString().split('T')[0];
20
+ const high = findings.filter((f) => f.severity === 'high');
21
+ const medium = findings.filter((f) => f.severity === 'medium');
22
+ const low = findings.filter((f) => f.severity === 'low');
23
+ const renderGroup = (label, emoji, group) => {
24
+ if (group.length === 0)
25
+ return '';
26
+ const rows = group.map((f) => `| ${f.issue} | ${f.recommendation} |`).join('\n');
27
+ return `\n## ${emoji} ${label} (${group.length})\n\n| Issue | Recommendation |\n|---|---|\n${rows}\n`;
28
+ };
29
+ return [
30
+ `# infrawise report — ${projectName}`,
31
+ `_Generated: ${date}_`,
32
+ '',
33
+ `**${findings.length} finding(s)**: ${high.length} high · ${medium.length} medium · ${low.length} low`,
34
+ renderGroup('High severity', '🔴', high),
35
+ renderGroup('Medium severity', '🟡', medium),
36
+ renderGroup('Low severity', '🟢', low),
37
+ '',
38
+ '_Generated by [infrawise](https://github.com/Sidd27/infrawise) — MCP server for AWS infrastructure analysis_',
39
+ ].filter((l) => l !== undefined).join('\n');
40
+ }
16
41
  function mkSpinner(text) {
17
42
  return ora({ text: chalk.dim(text), color: 'cyan' }).start();
18
43
  }
@@ -28,6 +53,7 @@ export async function runAnalyze(options = {}) {
28
53
  process.exit(1);
29
54
  }
30
55
  const repoPath = options.repo ?? process.cwd();
56
+ const minSeverity = options.severity ? SEVERITY_ORDER[options.severity] ?? 1 : 0;
31
57
  const awsCfg = { region: config.aws?.region, profile: config.aws?.profile, endpoint: config.aws?.endpoint };
32
58
  const dynamoMeta = [];
33
59
  const postgresMeta = [];
@@ -278,18 +304,30 @@ export async function runAnalyze(options = {}) {
278
304
  writeCache('operations', operations);
279
305
  writeCache('meta', { dynamoMeta, postgresMeta, mysqlMeta, mongoMeta, servicesMeta });
280
306
  // ── Output ────────────────────────────────────────────────────────────────────
307
+ const displayFindings = minSeverity > 0
308
+ ? findings.filter((f) => (SEVERITY_ORDER[f.severity] ?? 0) >= minSeverity)
309
+ : findings;
281
310
  console.log('');
282
- if (findings.length === 0) {
283
- console.log(` ${chalk.green.bold('✓ No issues found!')} ${chalk.dim('Your infrastructure looks clean.')}`);
311
+ if (displayFindings.length === 0) {
312
+ const msg = minSeverity > 0 ? `No ${options.severity} (or higher) severity issues found.` : 'Your infrastructure looks clean.';
313
+ console.log(` ${chalk.green.bold('✓ No issues found!')} ${chalk.dim(msg)}`);
284
314
  }
285
315
  else {
286
- console.log(chalk.bold(` Findings`) + chalk.dim(` ${findings.length} total`));
287
- findings.forEach((f, i) => printFinding(f, i));
288
- printSummaryBox(findings);
289
- if (findings.some((f) => f.severity === 'high')) {
316
+ const filterNote = minSeverity > 0 ? chalk.dim(` (${options.severity}+ only)`) : '';
317
+ console.log(chalk.bold(` Findings`) + chalk.dim(` ${displayFindings.length} total`) + filterNote);
318
+ displayFindings.forEach((f, i) => printFinding(f, i));
319
+ printSummaryBox(displayFindings);
320
+ if (displayFindings.some((f) => f.severity === 'high')) {
290
321
  console.log(`\n ${chalk.red.bold('Action required:')} ${chalk.red('High severity issues detected.')}`);
291
322
  }
292
323
  }
324
+ if (options.output) {
325
+ const report = buildMarkdownReport(displayFindings, config.project);
326
+ const outPath = path.resolve(options.output);
327
+ fs.writeFileSync(outPath, report, 'utf8');
328
+ console.log('');
329
+ log.success('Report saved', outPath);
330
+ }
293
331
  console.log('');
294
332
  log.dim(`Results cached in .infrawise/cache/`);
295
333
  log.info(`Run ${chalk.cyan('infrawise dev')} to explore via the MCP server`);
@@ -2,7 +2,7 @@ import chalk from 'chalk';
2
2
  import inquirer from 'inquirer';
3
3
  import ora from 'ora';
4
4
  import { readAWSProfiles, log, printHeader } from '../utils.js';
5
- import { validateDynamoAccess } from '../../adapters/dynamodb.js';
5
+ import { validateDynamoAccess } from '../../adapters/aws/dynamodb.js';
6
6
  export async function runAuth() {
7
7
  printHeader('AWS Authentication');
8
8
  const profiles = readAWSProfiles();
@@ -54,7 +54,7 @@ function groupTools(tools) {
54
54
  return lines;
55
55
  }
56
56
  export async function runDev(options = {}) {
57
- const port = options.port ?? 3000;
57
+ const port = options.port ?? (process.env.PORT ? parseInt(process.env.PORT, 10) : 3000);
58
58
  printHeader('MCP Server');
59
59
  let config;
60
60
  try {
@@ -4,12 +4,12 @@ import * as os from 'os';
4
4
  import chalk from 'chalk';
5
5
  import ora from 'ora';
6
6
  import { loadConfig } from '../../core/index.js';
7
- import { probeDynamoAccess } from '../../adapters/dynamodb.js';
8
- import { validatePostgresAccess } from '../../adapters/postgres.js';
9
- import { validateMySQLAccess } from '../../adapters/mysql.js';
10
- import { validateMongoAccess } from '../../adapters/mongodb.js';
11
- import { validateSQSAccess, validateSNSAccess, validateSSMAccess, validateSecretsAccess, validateLambdaAccess, validateEventBridgeAccess, } from '../../adapters/aws.js';
12
- import { validateLogsAccess } from '../../adapters/logs.js';
7
+ import { probeDynamoAccess } from '../../adapters/aws/dynamodb.js';
8
+ import { validatePostgresAccess } from '../../adapters/db/postgres.js';
9
+ import { validateMySQLAccess } from '../../adapters/db/mysql.js';
10
+ import { validateMongoAccess } from '../../adapters/db/mongodb.js';
11
+ import { validateSQSAccess, validateSNSAccess, validateSSMAccess, validateSecretsAccess, validateLambdaAccess, validateEventBridgeAccess, } from '../../adapters/aws/services.js';
12
+ import { validateLogsAccess } from '../../adapters/aws/logs.js';
13
13
  import { printHeader } from '../utils.js';
14
14
  async function runCheck(label, fn) {
15
15
  const spin = ora({ text: chalk.dim(label), color: 'cyan' }).start();
@@ -0,0 +1,28 @@
1
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
2
+ import { loadConfig, formatError, readCache } from '../../core/index.js';
3
+ import { createMcpServer, setGraphState } from '../../server/index.js';
4
+ import { runAnalyze } from './analyze.js';
5
+ export async function runStdio(configPath) {
6
+ let config;
7
+ try {
8
+ config = loadConfig(configPath);
9
+ }
10
+ catch (err) {
11
+ process.stderr.write(formatError(err) + '\n');
12
+ process.exit(1);
13
+ }
14
+ const cachedGraph = readCache('graph');
15
+ const cachedFindings = readCache('findings');
16
+ if (cachedGraph && cachedFindings) {
17
+ setGraphState(cachedGraph, cachedFindings, config);
18
+ }
19
+ else {
20
+ await runAnalyze({ config: configPath });
21
+ const graph = readCache('graph') ?? { nodes: [], edges: [] };
22
+ const findings = readCache('findings') ?? [];
23
+ setGraphState(graph, findings, config);
24
+ }
25
+ const mcp = createMcpServer();
26
+ const transport = new StdioServerTransport();
27
+ await mcp.connect(transport);
28
+ }
package/dist/cli/index.js CHANGED
@@ -8,6 +8,7 @@ import { runAuth } from './commands/auth.js';
8
8
  import { runAnalyze } from './commands/analyze.js';
9
9
  import { runDev } from './commands/dev.js';
10
10
  import { runDoctor } from './commands/doctor.js';
11
+ import { runStdio } from './commands/stdio.js';
11
12
  const { version } = JSON.parse(readFileSync(join(import.meta.dirname, '../../package.json'), 'utf8'));
12
13
  const program = new Command();
13
14
  program
@@ -35,12 +36,16 @@ program
35
36
  .option('-c, --config <path>', 'Path to infrawise.yaml', 'infrawise.yaml')
36
37
  .option('-r, --repo <path>', 'Path to repository to scan', process.cwd())
37
38
  .option('--no-cache', 'Skip reading/writing the cache')
39
+ .option('-o, --output <path>', 'Save findings as a markdown report (e.g. report.md)')
40
+ .option('--severity <level>', 'Only show findings at or above this level: high | medium | low')
38
41
  .action(async (options) => {
39
42
  printBanner();
40
43
  await runAnalyze({
41
44
  config: options.config !== 'infrawise.yaml' ? options.config : undefined,
42
45
  repo: options.repo,
43
46
  noCache: !options.cache,
47
+ output: options.output,
48
+ severity: options.severity,
44
49
  });
45
50
  });
46
51
  program
@@ -55,6 +60,13 @@ program
55
60
  port: parseInt(options.port, 10),
56
61
  });
57
62
  });
63
+ program
64
+ .command('stdio')
65
+ .description('Start MCP server on stdio transport (for Claude Desktop and Glama)')
66
+ .option('-c, --config <path>', 'Path to infrawise.yaml', 'infrawise.yaml')
67
+ .action(async (options) => {
68
+ await runStdio(options.config !== 'infrawise.yaml' ? options.config : undefined);
69
+ });
58
70
  program
59
71
  .command('doctor')
60
72
  .description('Validate AWS access, postgres connectivity, config, and repo scan')
@@ -31,7 +31,7 @@ function logged(name, fn) {
31
31
  export function createMcpServer() {
32
32
  const mcp = new McpServer({ name: 'infrawise', version });
33
33
  mcp.registerTool('get_infra_overview', {
34
- description: 'Returns a complete snapshot of all infrastructure: databases, queues, topics, secrets, parameters, log groups, lambdas, and all findings. Start here for a full picture.',
34
+ description: 'Returns a compact infrastructure snapshot: service counts, all databases, queues, topics, secrets, lambdas, and high-severity findings. Call this first at the start of any database or infrastructure task to understand what services are in scope. Prefer this over get_graph_summary for quick orientation; use get_graph_summary only when you need every node, edge, and finding in full.',
35
35
  inputSchema: z.object({}),
36
36
  }, logged('get_infra_overview', async () => {
37
37
  const tables = getTableNodes(currentGraph);
@@ -62,7 +62,7 @@ export function createMcpServer() {
62
62
  });
63
63
  }));
64
64
  mcp.registerTool('get_graph_summary', {
65
- description: 'Returns the full infrastructure graph (all nodes and edges) plus findings summary.',
65
+ description: 'Returns every node (tables, functions, lambdas, queues, etc.), every edge (query, scan, triggers, publishes_to), and all findings. Use this when you need to trace relationships across multiple services or require the complete finding set — not just high-severity ones. For a quick overview use get_infra_overview instead.',
66
66
  inputSchema: z.object({}),
67
67
  }, logged('get_graph_summary', async () => toText({
68
68
  nodes: currentGraph.nodes,
@@ -76,7 +76,7 @@ export function createMcpServer() {
76
76
  },
77
77
  })));
78
78
  mcp.registerTool('analyze_function', {
79
- description: 'Analyze a specific function for all infrastructure issues: DB queries, queue publishing, secret access, trigger event shapes, etc.',
79
+ description: 'Analyzes a single named function or Lambda handler for infrastructure issues: which tables it queries, how it queries them (scan vs query), queue publishing, secret access, and the correct event shape for each trigger (SQS, DynamoDB Streams, Kinesis, EventBridge). Call this before writing or reviewing a Lambda handler to get the exact trigger event shape and all findings scoped to this function. Returns found: false if the function name was not discovered during analysis.',
80
80
  inputSchema: z.object({ function: z.string().describe('Function name to analyze') }),
81
81
  }, logged('analyze_function', async ({ function: functionName }) => {
82
82
  const funcNode = currentGraph.nodes.find((n) => n.type === 'function' && n.name === functionName);
@@ -107,7 +107,7 @@ export function createMcpServer() {
107
107
  });
108
108
  }));
109
109
  mcp.registerTool('suggest_gsi', {
110
- description: 'Get GSI suggestions for a DynamoDB table and attribute',
110
+ description: 'Generates a ready-to-use DynamoDB GSI definition — index name, partition key, projection type, billing mode — for a given table and attribute. Call this when a query pattern needs an index that does not exist yet, or when the analyzer flags a missing GSI finding. Does not verify whether the GSI already exists; check the table schema in get_infra_overview first.',
111
111
  inputSchema: z.object({
112
112
  table: z.string().describe('DynamoDB table name'),
113
113
  attribute: z.string().describe('Attribute to create the GSI on'),
@@ -125,7 +125,7 @@ export function createMcpServer() {
125
125
  });
126
126
  }));
127
127
  mcp.registerTool('postgres_index_suggestions', {
128
- description: 'Get PostgreSQL index suggestions for a table column',
128
+ description: 'Generates the exact CREATE INDEX CONCURRENTLY SQL for a PostgreSQL table column, including a partial index variant and a post-creation ANALYZE reminder. Call this when the analyzer flags a missing index finding or when writing a query that filters on a column without an existing index. Does not verify whether the index already exists.',
129
129
  inputSchema: z.object({
130
130
  table: z.string().describe('PostgreSQL table name'),
131
131
  column: z.string().describe('Column name to index'),
@@ -143,7 +143,7 @@ export function createMcpServer() {
143
143
  });
144
144
  }));
145
145
  mcp.registerTool('suggest_mongo_index', {
146
- description: 'Get index suggestions for a MongoDB collection field',
146
+ description: 'Generates the exact db.collection.createIndex() command for a MongoDB field, plus compound and text index variants and an explain query to verify. Call this when a collection scan is flagged by the analyzer or when writing a query that filters on an unindexed field. Does not check whether the index already exists.',
147
147
  inputSchema: z.object({
148
148
  collection: z.string().describe('MongoDB collection name'),
149
149
  field: z.string().describe('Field name to index'),
@@ -163,7 +163,7 @@ export function createMcpServer() {
163
163
  });
164
164
  }));
165
165
  mcp.registerTool('mysql_index_suggestions', {
166
- description: 'Get MySQL index suggestions for a table column',
166
+ description: 'Generates the exact ALTER TABLE ADD INDEX SQL for a MySQL table column, including a composite variant and EXPLAIN guidance to verify the index is used. Call this when the analyzer flags a missing MySQL index or full table scan finding. Does not verify whether the index already exists.',
167
167
  inputSchema: z.object({
168
168
  table: z.string().describe('MySQL table name'),
169
169
  column: z.string().describe('Column name to index'),
@@ -181,7 +181,7 @@ export function createMcpServer() {
181
181
  });
182
182
  }));
183
183
  mcp.registerTool('get_queue_details', {
184
- description: 'Returns all SQS queues with DLQ status, encryption, message counts, and retention.',
184
+ description: 'Returns all SQS queues with DLQ presence, encryption status, approximate message count, and retention days. Call this when reviewing messaging architecture, investigating a message backlog, or checking DLQ coverage before adding a new consumer. Use get_infra_overview for a quick queue count only.',
185
185
  inputSchema: z.object({}),
186
186
  }, logged('get_queue_details', async () => {
187
187
  const queues = getQueueNodes(currentGraph);
@@ -196,14 +196,14 @@ export function createMcpServer() {
196
196
  });
197
197
  }));
198
198
  mcp.registerTool('get_topic_details', {
199
- description: 'Returns all SNS topics with subscription counts and protocols.',
199
+ description: 'Returns all SNS topics with subscription count and encryption status. Call this when reviewing event fan-out patterns or checking whether a topic has the expected number of subscribers.',
200
200
  inputSchema: z.object({}),
201
201
  }, logged('get_topic_details', async () => {
202
202
  const topics = getTopicNodes(currentGraph);
203
203
  return toText({ total: topics.length, topics: topics.map((t) => ({ name: t.name, provider: t.provider, subscriptionCount: t.subscriptionCount, encrypted: t.encrypted })) });
204
204
  }));
205
205
  mcp.registerTool('get_secrets_overview', {
206
- description: 'Returns all Secrets Manager secrets: names, rotation status. Secret VALUES are never included.',
206
+ description: 'Returns all Secrets Manager secrets with rotation status and rotation interval. Secret values are never returned. Call this when checking which secrets exist, confirming rotation is enabled before a security review, or identifying secrets that lack rotation.',
207
207
  inputSchema: z.object({}),
208
208
  }, logged('get_secrets_overview', async () => {
209
209
  const secrets = getSecretNodes(currentGraph);
@@ -217,7 +217,7 @@ export function createMcpServer() {
217
217
  });
218
218
  }));
219
219
  mcp.registerTool('get_parameter_overview', {
220
- description: 'Returns all SSM Parameter Store parameters: names, types, tiers. Parameter VALUES are never included.',
220
+ description: 'Returns all SSM Parameter Store parameters with type (String, SecureString, StringList) and tier (Standard, Advanced). Parameter values are never returned. Call this when checking which config parameters exist for a service or verifying parameter types.',
221
221
  inputSchema: z.object({}),
222
222
  }, logged('get_parameter_overview', async () => {
223
223
  const parameters = getParameterNodes(currentGraph);
@@ -227,7 +227,7 @@ export function createMcpServer() {
227
227
  });
228
228
  }));
229
229
  mcp.registerTool('get_lambda_overview', {
230
- description: 'Returns all Lambda functions: runtime, memory, timeout, env var key names (values never included), and known event source triggers with correct handler event shapes.',
230
+ description: 'Returns all Lambda functions with runtime, memory (MB), timeout (sec), environment variable key names (values never returned), and event source triggers with the correct handler event shape for each. Call this when auditing Lambda configuration for default memory (128 MB) or high timeouts, or when you need the trigger event shape for a specific function without running analyze_function.',
231
231
  inputSchema: z.object({}),
232
232
  }, logged('get_lambda_overview', async () => {
233
233
  const lambdas = getLambdaNodes(currentGraph);
@@ -243,7 +243,7 @@ export function createMcpServer() {
243
243
  });
244
244
  }));
245
245
  mcp.registerTool('get_eventbridge_details', {
246
- description: 'Returns all EventBridge rules: name, state, schedule expression or event pattern, and target Lambda functions.',
246
+ description: 'Returns all EventBridge rules with name, ENABLED/DISABLED state, schedule expression (rate/cron rules), event pattern (event-driven rules), and target Lambda function names. Call this when checking what schedule or event triggers a Lambda, or when reviewing rule coverage across the account.',
247
247
  inputSchema: z.object({}),
248
248
  }, logged('get_eventbridge_details', async () => {
249
249
  const rules = getEventBridgeRuleNodes(currentGraph);
@@ -263,7 +263,7 @@ export function createMcpServer() {
263
263
  });
264
264
  }));
265
265
  mcp.registerTool('get_log_errors', {
266
- description: 'Returns recent error patterns from CloudWatch log groups. Returns pattern counts and frequencies never raw log messages.',
266
+ description: 'Returns recent error pattern summaries from CloudWatch log groups: pattern counts and frequencies grouped by log group. Raw log messages are never returned. Use the optional logGroup filter to scope to one group by name substring. Call this when investigating errors or identifying log groups with no retention policy.',
267
267
  inputSchema: z.object({ logGroup: z.string().describe('Filter to a specific log group name (optional)').optional() }),
268
268
  }, logged('get_log_errors', async ({ logGroup: filterName }) => {
269
269
  const logGroups = getLogGroupNodes(currentGraph).filter((lg) => !filterName || lg.name.includes(filterName));
@@ -286,6 +286,22 @@ export function createServer(port = 3000) {
286
286
  graphEdges: currentGraph.edges.length,
287
287
  findings: currentFindings.length,
288
288
  }));
289
+ fastify.get('/.well-known/mcp/server-card.json', async () => ({
290
+ schema_version: '2026-01',
291
+ name: 'io.github.Sidd27/infrawise',
292
+ display_name: 'Infrawise',
293
+ version,
294
+ description: 'Infrastructure analysis MCP server — scans DynamoDB, PostgreSQL, MySQL, MongoDB, Lambda, SQS, SNS, EventBridge, Secrets Manager, SSM, CloudWatch, Terraform, CDK, and source code. Surfaces missing indexes, DLQ gaps, Lambda misconfig, and correct trigger event shapes.',
295
+ homepage: 'https://github.com/Sidd27/infrawise',
296
+ repository: 'https://github.com/Sidd27/infrawise',
297
+ transports: [{ type: 'streamable-http', url: `http://localhost:${port}/mcp` }],
298
+ tools: [
299
+ 'get_infra_overview', 'get_graph_summary', 'analyze_function',
300
+ 'suggest_gsi', 'postgres_index_suggestions', 'suggest_mongo_index', 'mysql_index_suggestions',
301
+ 'get_queue_details', 'get_topic_details', 'get_secrets_overview', 'get_parameter_overview',
302
+ 'get_lambda_overview', 'get_eventbridge_details', 'get_log_errors',
303
+ ],
304
+ }));
289
305
  fastify.post('/mcp', async (request, reply) => {
290
306
  const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined });
291
307
  reply.raw.on('close', () => transport.close());
package/package.json CHANGED
@@ -1,31 +1,41 @@
1
1
  {
2
2
  "name": "infrawise",
3
- "version": "0.7.0",
3
+ "version": "0.7.2",
4
4
  "mcpName": "io.github.Sidd27/infrawise",
5
5
  "description": "CLI-first infrastructure intelligence platform — analyzes DynamoDB, PostgreSQL, MySQL, MongoDB, SQS, SNS, SSM, Secrets Manager, Lambda, CloudWatch Logs and exposes findings as an MCP server for Claude Code",
6
6
  "keywords": [
7
+ "mcp",
8
+ "mcp-server",
9
+ "model-context-protocol",
7
10
  "infrastructure",
11
+ "infrastructure-analysis",
8
12
  "aws",
9
13
  "dynamodb",
10
14
  "sqs",
11
15
  "sns",
12
16
  "lambda",
17
+ "eventbridge",
13
18
  "cloudwatch",
14
19
  "secrets-manager",
15
20
  "ssm",
16
21
  "postgresql",
17
22
  "mysql",
18
23
  "mongodb",
19
- "mcp",
24
+ "serverless",
20
25
  "claude",
21
26
  "claude-code",
22
- "analysis",
23
- "cli",
27
+ "cursor",
28
+ "ai-coding",
29
+ "ai-tools",
24
30
  "devtools",
31
+ "cli",
25
32
  "terraform",
26
33
  "cloudformation",
27
34
  "cdk"
28
35
  ],
36
+ "agentskills": {
37
+ "infrawise": "./AGENTS.md"
38
+ },
29
39
  "homepage": "https://github.com/Sidd27/infrawise#readme",
30
40
  "repository": {
31
41
  "type": "git",
@@ -97,5 +107,10 @@
97
107
  ],
98
108
  "publishConfig": {
99
109
  "access": "public"
110
+ },
111
+ "pnpm": {
112
+ "overrides": {
113
+ "qs": ">=6.15.2"
114
+ }
100
115
  }
101
116
  }