honeymcp 0.1.3__py3-none-any.whl → 0.1.4__py3-none-any.whl

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.
honeymcp/api/app.py CHANGED
@@ -15,7 +15,7 @@ from pydantic import BaseModel, Field
15
15
 
16
16
  from honeymcp.models.config import HoneyMCPConfig
17
17
  from honeymcp.models.events import AttackFingerprint
18
- from honeymcp.storage.event_store import get_event, list_events
18
+ from honeymcp.storage.event_store import clear_events, get_event, list_events
19
19
 
20
20
 
21
21
  class EventListResponse(BaseModel):
@@ -48,6 +48,13 @@ class FiltersResponse(BaseModel):
48
48
  tools: List[str]
49
49
 
50
50
 
51
+ class ClearEventsResponse(BaseModel):
52
+ """Response returned when stored events are deleted."""
53
+
54
+ deleted_events: int
55
+ storage_path: str
56
+
57
+
51
58
  def _apply_filters(
52
59
  events: List[AttackFingerprint],
53
60
  threat_level: Optional[str],
@@ -177,6 +184,14 @@ def create_app(config_path: Optional[Path | str] = None) -> FastAPI:
177
184
  raise HTTPException(status_code=404, detail="Event not found")
178
185
  return event
179
186
 
187
+ @app.delete("/events", response_model=ClearEventsResponse)
188
+ async def delete_events() -> ClearEventsResponse:
189
+ deleted_count = await clear_events(storage_path=app.state.event_storage_path)
190
+ return ClearEventsResponse(
191
+ deleted_events=deleted_count,
192
+ storage_path=str(app.state.event_storage_path),
193
+ )
194
+
180
195
  @app.get("/metrics", response_model=MetricsResponse)
181
196
  async def get_metrics(
182
197
  start_date: Optional[date] = Query(default=None),
honeymcp/cli.py CHANGED
@@ -1,9 +1,13 @@
1
1
  """HoneyMCP CLI - Command line tools for HoneyMCP setup and management."""
2
2
 
3
+ import asyncio
3
4
  import argparse
4
5
  import sys
5
6
  from pathlib import Path
6
7
 
8
+ from honeymcp.models.config import HoneyMCPConfig
9
+ from honeymcp.storage.event_store import clear_events
10
+
7
11
  CONFIG_TEMPLATE = """\
8
12
  # HoneyMCP Configuration
9
13
  # ======================
@@ -156,6 +160,27 @@ def cmd_version(args: argparse.Namespace) -> int:
156
160
  return 0
157
161
 
158
162
 
163
+ def cmd_clean_data(args: argparse.Namespace) -> int:
164
+ """Delete stored HoneyMCP attack events."""
165
+ storage_path: Path
166
+ if args.path:
167
+ storage_path = Path(args.path).expanduser()
168
+ else:
169
+ config = HoneyMCPConfig.load(args.config)
170
+ storage_path = config.event_storage_path
171
+
172
+ if not args.yes:
173
+ print(f"This will permanently delete all stored events in: {storage_path}")
174
+ confirm = input("Continue? [y/N]: ").strip().lower()
175
+ if confirm not in {"y", "yes"}:
176
+ print("Aborted.")
177
+ return 0
178
+
179
+ deleted_count = asyncio.run(clear_events(storage_path=storage_path))
180
+ print(f"Deleted {deleted_count} event file(s) from {storage_path}")
181
+ return 0
182
+
183
+
159
184
  def main() -> int:
160
185
  """Main CLI entry point."""
161
186
  parser = argparse.ArgumentParser(
@@ -191,6 +216,29 @@ def main() -> int:
191
216
  )
192
217
  version_parser.set_defaults(func=cmd_version)
193
218
 
219
+ clean_data_parser = subparsers.add_parser(
220
+ "clean-data",
221
+ help="Delete all stored attack event data",
222
+ description="Remove persisted event JSON files from HoneyMCP storage",
223
+ )
224
+ clean_data_parser.add_argument(
225
+ "--path",
226
+ default=None,
227
+ help="Event storage directory (overrides config and env resolution)",
228
+ )
229
+ clean_data_parser.add_argument(
230
+ "--config",
231
+ default=None,
232
+ help="Path to honeymcp.yaml (optional, default lookup order applies)",
233
+ )
234
+ clean_data_parser.add_argument(
235
+ "-y",
236
+ "--yes",
237
+ action="store_true",
238
+ help="Skip confirmation prompt",
239
+ )
240
+ clean_data_parser.set_defaults(func=cmd_clean_data)
241
+
194
242
  # Parse and execute
195
243
  args = parser.parse_args()
196
244
 
@@ -38,6 +38,7 @@ async def fingerprint_attack(
38
38
  arguments: Dict[str, Any],
39
39
  context: Any,
40
40
  ghost_spec: GhostToolSpec,
41
+ response_sent: Optional[str] = None,
41
42
  ) -> AttackFingerprint:
42
43
  """Capture complete attack context when a ghost tool is triggered.
43
44
 
@@ -61,8 +62,8 @@ async def fingerprint_attack(
61
62
  # Extract client metadata
62
63
  client_metadata = _extract_client_metadata(context)
63
64
 
64
- # Generate fake response
65
- fake_response = ghost_spec.response_generator(arguments)
65
+ # Use the exact response that was returned by middleware when provided.
66
+ fake_response = response_sent or ghost_spec.response_generator(arguments)
66
67
 
67
68
  # Create unique event ID
68
69
  event_id = f"evt_{datetime.utcnow().strftime('%Y%m%d_%H%M%S')}_{uuid4().hex[:8]}"
@@ -287,8 +287,8 @@ def honeypot( # pylint: disable=too-many-arguments,too-many-positional-argument
287
287
  else dynamic_ghost_specs.get(name)
288
288
  )
289
289
 
290
- # Use standard fake response
291
- fake_response = ghost_spec.response_generator(arguments or {})
290
+ # Use one response value for both MCP return and stored event.
291
+ fake_response = ghost_spec.response_generator(resolved_arguments or {})
292
292
 
293
293
  # Capture attack fingerprint
294
294
  fingerprint = await fingerprint_attack(
@@ -296,6 +296,7 @@ def honeypot( # pylint: disable=too-many-arguments,too-many-positional-argument
296
296
  arguments=resolved_arguments or {},
297
297
  context=context,
298
298
  ghost_spec=ghost_spec,
299
+ response_sent=fake_response,
299
300
  )
300
301
 
301
302
  # ATTACK DETECTED! Mark session as attacker and log details
@@ -159,7 +159,7 @@ function EventRow({ event }) {
159
159
  );
160
160
  }
161
161
 
162
- function FilterBar({ filters, options, onChange, onRefresh, loading, apiBase, onApiBaseChange }) {
162
+ function FilterBar({ filters, options, onChange, onRefresh, onClear, loading, clearing, apiBase, onApiBaseChange }) {
163
163
  return (
164
164
  <section className="panel glass-panel">
165
165
  <div className="grid filters">
@@ -186,6 +186,12 @@ function FilterBar({ filters, options, onChange, onRefresh, loading, apiBase, on
186
186
  {loading ? "Refreshing..." : "Apply Filters"}
187
187
  </button>
188
188
  </div>
189
+
190
+ <div className="form-group">
191
+ <button className="danger" onClick={onClear} disabled={clearing || loading}>
192
+ {clearing ? "Clearing..." : "Clear Stored Data"}
193
+ </button>
194
+ </div>
189
195
  </div>
190
196
  </section>
191
197
  );
@@ -222,13 +228,15 @@ function App() {
222
228
  });
223
229
 
224
230
  const [loading, setLoading] = useState(false);
231
+ const [clearing, setClearing] = useState(false);
225
232
  const [error, setError] = useState(null);
233
+ const [notice, setNotice] = useState(null);
226
234
 
227
235
  // Data Fetching
228
- const fetchJson = useCallback(async (path, queryParams = {}) => {
236
+ const fetchJson = useCallback(async (path, queryParams = {}, requestOptions = {}) => {
229
237
  const url = `${apiBase}${path}${buildQuery(queryParams)}`;
230
238
  try {
231
- const response = await fetch(url);
239
+ const response = await fetch(url, requestOptions);
232
240
  if (!response.ok) throw new Error(`HTTP ${response.status}`);
233
241
  return await response.json();
234
242
  } catch (err) {
@@ -239,6 +247,7 @@ function App() {
239
247
  const loadData = useCallback(async () => {
240
248
  setLoading(true);
241
249
  setError(null);
250
+ setNotice(null);
242
251
  try {
243
252
  // Parallel fetch
244
253
  const [filterData, metricsData, eventsResp] = await Promise.all([
@@ -281,6 +290,28 @@ function App() {
281
290
  setFilters(prev => ({ ...prev, [name]: value }));
282
291
  };
283
292
 
293
+ const handleClearData = async () => {
294
+ const shouldDelete = window.confirm("Delete all stored HoneyMCP events?");
295
+ if (!shouldDelete) {
296
+ return;
297
+ }
298
+
299
+ setClearing(true);
300
+ setError(null);
301
+ setNotice(null);
302
+
303
+ try {
304
+ const result = await fetchJson("/events", {}, { method: "DELETE" });
305
+ setNotice(`Deleted ${result.deleted_events || 0} event(s).`);
306
+ await loadData();
307
+ } catch (err) {
308
+ console.error(err);
309
+ setError("Unable to clear stored events.");
310
+ } finally {
311
+ setClearing(false);
312
+ }
313
+ };
314
+
284
315
  return (
285
316
  <div className="container">
286
317
  <header className="header">
@@ -300,11 +331,19 @@ function App() {
300
331
  options={options}
301
332
  onChange={handleFilterChange}
302
333
  onRefresh={loadData}
334
+ onClear={handleClearData}
303
335
  loading={loading}
336
+ clearing={clearing}
304
337
  apiBase={apiBase}
305
338
  onApiBaseChange={setApiBase}
306
339
  />
307
340
 
341
+ {notice && (
342
+ <div className="panel panel-notice">
343
+ <span>{notice}</span>
344
+ </div>
345
+ )}
346
+
308
347
  {error && (
309
348
  <div className="panel" style={{ borderColor: 'var(--critical-border)', background: 'var(--critical-bg)', color: 'var(--critical-text)' }}>
310
349
  <div style={{ display: 'flex', gap: '12px', alignItems: 'center' }}>
@@ -236,9 +236,26 @@
236
236
  box-shadow: 0 6px 8px rgba(217, 119, 6, 0.4);
237
237
  }
238
238
 
239
- button.primary:active {
240
- transform: translateY(0);
241
- }
239
+ button.primary:active {
240
+ transform: translateY(0);
241
+ }
242
+
243
+ button.danger {
244
+ background: linear-gradient(135deg, #b91c1c 0%, #991b1b 100%);
245
+ color: white;
246
+ border: none;
247
+ font-weight: 600;
248
+ cursor: pointer;
249
+ }
250
+
251
+ button.danger:hover {
252
+ background: linear-gradient(135deg, #dc2626 0%, #b91c1c 100%);
253
+ }
254
+
255
+ button:disabled {
256
+ opacity: 0.6;
257
+ cursor: not-allowed;
258
+ }
242
259
 
243
260
  /* Metrics */
244
261
  .metric-card {
@@ -429,11 +446,17 @@
429
446
  }
430
447
 
431
448
  /* Fallback/Empty States */
432
- .empty-state {
433
- text-align: center;
434
- padding: 64px 0;
435
- color: var(--text-secondary);
436
- }
449
+ .empty-state {
450
+ text-align: center;
451
+ padding: 64px 0;
452
+ color: var(--text-secondary);
453
+ }
454
+
455
+ .panel-notice {
456
+ border-color: rgba(134, 239, 172, 0.5);
457
+ background: rgba(22, 101, 52, 0.18);
458
+ color: #bbf7d0;
459
+ }
437
460
 
438
461
  /* Loading Skeleton */
439
462
  .skeleton {
@@ -509,4 +532,4 @@
509
532
 
510
533
  ::-webkit-scrollbar-thumb:hover {
511
534
  background-color: var(--slate-600);
512
- }
535
+ }
@@ -174,3 +174,38 @@ async def update_event(
174
174
  continue
175
175
 
176
176
  return False
177
+
178
+
179
+ async def clear_events(storage_path: Optional[Path] = None) -> int:
180
+ """Delete all persisted attack events.
181
+
182
+ Args:
183
+ storage_path: Base directory for event storage
184
+
185
+ Returns:
186
+ Number of event JSON files deleted
187
+ """
188
+ storage_path = resolve_event_storage_path(storage_path)
189
+ if not storage_path.exists():
190
+ return 0
191
+
192
+ deleted_count = 0
193
+
194
+ for date_dir in storage_path.iterdir():
195
+ if not date_dir.is_dir():
196
+ continue
197
+
198
+ for json_file in date_dir.glob("*.json"):
199
+ try:
200
+ json_file.unlink()
201
+ deleted_count += 1
202
+ except Exception as e:
203
+ print(f"Warning: Failed to delete {json_file}: {e}")
204
+
205
+ try:
206
+ date_dir.rmdir()
207
+ except OSError:
208
+ # Keep directory when it still has non-event files/subdirs.
209
+ continue
210
+
211
+ return deleted_count
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: honeymcp
3
- Version: 0.1.3
3
+ Version: 0.1.4
4
4
  Summary: Deception middleware for AI agents - detecting data theft and indirect prompt injection in MCP servers
5
5
  Project-URL: Homepage, https://github.com/barvhaim/HoneyMCP
6
6
  Project-URL: Documentation, https://github.com/barvhaim/HoneyMCP#readme
@@ -133,6 +133,8 @@ MCP_TRANSPORT=sse uv run python examples/demo_server_dynamic.py
133
133
  make run-ui
134
134
  ```
135
135
 
136
+ <img width="1426" height="972" alt="image" src="https://github.com/user-attachments/assets/2dfc37a2-8caa-4338-b7f7-1cbac7ed9d79" />
137
+
136
138
  ---
137
139
 
138
140
  ## 🎭 How It Works
@@ -275,8 +277,18 @@ mcp = honeypot(mcp, protection_mode=ProtectionMode.COGNITIVE)
275
277
  The easiest way to configure HoneyMCP:
276
278
  ```bash
277
279
  honeymcp init # Creates honeymcp.yaml + .env.honeymcp
280
+ # Optional: remove all persisted attack event files
281
+ honeymcp clean-data
278
282
  ```
279
283
 
284
+ ### Clear Stored Events
285
+
286
+ You can remove all persisted event JSON files from CLI, API, or UI:
287
+
288
+ - CLI: `honeymcp clean-data`
289
+ - API: `DELETE /events`
290
+ - Dashboard: Use the **Clear Stored Data** button
291
+
280
292
  ### YAML Config
281
293
 
282
294
  ```yaml
@@ -551,244 +563,17 @@ The new tool is immediately available in your honeypot catalog.
551
563
 
552
564
  ---
553
565
 
554
- ## ❓ FAQ
555
-
556
- ### Can attackers detect the honeypots?
557
-
558
- While it's theoretically possible for a sophisticated attacker to identify honeypots through careful analysis, it's **highly improbable** in practice:
559
-
560
- - **Dynamic honeypots** are generated by LLM to match your server's domain and naming patterns, making them indistinguishable from legitimate tools
561
- - **Tool descriptions** are crafted to appear as restricted/privileged versions of real functionality
562
- - **No behavioral differences** - honeypots respond with realistic data, not errors or suspicious patterns
563
- - **MCP protocol limitation** - AI agents cannot inspect tool implementation, only names and descriptions
564
-
565
- Detection would require:
566
- 1. Prior knowledge of your specific tool catalog
567
- 2. Ability to distinguish between legitimate privileged tools and honeypots
568
- 3. Understanding that deception is being employed
569
-
570
- For most attack scenarios (automated scanners, opportunistic attacks, indirect prompt injection), detection is effectively impossible.
571
-
572
- ### Will this slow down my MCP server?
573
-
574
- No. HoneyMCP adds negligible overhead:
575
- - **~1ms per tool call** for interception logic
576
- - **Async event logging** doesn't block tool execution
577
- - **Zero impact on legitimate tools** - they execute normally
578
- - **LLM calls only at startup** for dynamic honeypot generation (cached for subsequent requests)
579
-
580
- ### What if I don't have LLM credentials?
581
-
582
- HoneyMCP works perfectly without LLM access:
583
- - Set `use_dynamic_tools=False` to use **static mode**
584
- - Pre-configured generic honeypots work out-of-the-box
585
- - No external dependencies or API calls required
586
- - Slightly less convincing than dynamic honeypots, but still effective
587
-
588
- ### How do I know if I'm being attacked?
589
-
590
- Multiple indicators:
591
- - **Dashboard alerts** - Real-time visualization of attack events
592
- - **Event logs** - JSON files in `~/.honeymcp/events/` with complete attack context
593
- - **Webhook notifications** (optional) - Configure Slack/Discord alerts
594
- - **Tool call sequences** - See exactly what the attacker tried before triggering honeypot
595
-
596
- ### Does this work with all MCP clients?
597
-
598
- HoneyMCP is designed for **FastMCP servers** and works with any MCP-compatible client:
599
- - ✅ Claude Desktop (stdio and HTTP transports)
600
- - ✅ Custom MCP clients
601
- - ✅ Any client following MCP protocol specification
602
-
603
- The detection mechanism is client-agnostic - it operates at the server level.
604
-
605
- ### What's the difference between SCANNER and COGNITIVE modes?
606
-
607
- **SCANNER mode (default):**
608
- - Immediate lockout after honeypot trigger
609
- - All subsequent tools return errors
610
- - Best for automated attacks and quick containment
611
-
612
- **COGNITIVE mode:**
613
- - Sustained deception after honeypot trigger
614
- - Real tools return synthetic/mock data
615
- - Keeps attacker engaged for intelligence gathering
616
- - Best for sophisticated attacks and red team exercises
566
+ ## Documentation
617
567
 
568
+ - [FAQ](docs/faq.md)
569
+ - [Architecture](docs/architecture.md)
570
+ - [Use Cases](docs/use-cases.md)
571
+ - [Security Considerations](docs/security-considerations.md)
572
+ - [Development](docs/development.md)
573
+ - [CLI Reference](docs/cli-reference.md)
618
574
  ---
619
-
620
- ## 🏗️ Architecture
621
-
622
- ```
623
- ┌─────────────────────────────────────────────────────────────┐
624
- │ AI Agent (Claude) │
625
- └────────────────────┬───────────────────────▲────────────────┘
626
- │ │
627
- │ MCP Protocol │
628
- ▼ │
629
- ┌─────────────────────────────────────────────────────────────┐
630
- │ HoneyMCP Middleware │
631
- │ ┌────────────────────────────────────────────────────────┐ │
632
- │ │ Tool Call Interceptor │ │
633
- │ │ ├─ Is ghost tool? │ │
634
- │ │ │ YES: Fingerprint + Store + Return fake data │ │
635
- │ │ │ NO: Pass through to legitimate tool │ │
636
- │ └────────────────────────────────────────────────────────┘ │
637
- │ │
638
- │ Ghost Tools: [list_cloud_secrets, execute_shell_command] │
639
- │ Real Tools: [safe_calculator, get_weather, ...] │
640
- └─────────────────────────────────────────────────────────────┘
641
- │ ▲
642
- ▼ │
643
- ┌──────────────────┐ ┌──────────────────┐
644
- │ Event Storage │ │ Your Real Tools │
645
- │ ~/.honeymcp/ │ │ │
646
- └──────────────────┘ └──────────────────┘
647
-
648
-
649
- ┌──────────────────┐
650
- │ React │
651
- │ Dashboard │
652
- └──────────────────┘
653
- ```
654
-
655
575
  ---
656
576
 
657
- ## 🎓 Use Cases
658
-
659
- ### 1. Production Monitoring
660
- Deploy HoneyMCP in production to detect attacks targeting your AI agents:
661
- - **Customer support bots** - Detect attempts to exfiltrate customer data or inject malicious responses
662
- - **Internal AI assistants** - Catch data theft attempts targeting internal credentials or documents
663
- - **Code generation tools** - Detect injection of malicious code or unauthorized file access
664
- - **Data analysis agents** - Identify attempts to steal sensitive datasets or manipulate outputs
665
-
666
- ### 2. Red Team Testing
667
- Use HoneyMCP to validate your AI security defenses:
668
- - Test if your AI filters catch data exfiltration attempts
669
- - Measure indirect prompt injection success rates
670
- - Gather TTPs for threat modeling
671
-
672
- ### 3. Security Research
673
- Study AI agent attack techniques in the wild:
674
- - Capture real-world exfiltration patterns
675
- - Analyze indirect prompt injection payloads
676
- - Build threat intelligence database
677
-
678
- ### 4. Compliance & Auditing
679
- Demonstrate security controls for AI systems:
680
- - Prove attack detection capabilities for data theft and injection attacks
681
- - Generate audit logs of attempted attacks
682
- - Meet AI security compliance requirements
683
-
684
- ## Security Considerations
685
-
686
- ### Detection Capabilities
687
- ✅ Detects data exfiltration attempts via GET-style honeypots
688
- ✅ Detects indirect prompt injection via SET-style honeypots
689
- ✅ Captures complete attack context and telemetry
690
- ✅ Returns synthetic data to maintain deception
691
-
692
- ### Limitations
693
- ❌ Detection-only system (does not prevent attacks)
694
- ❌ Does not sanitize or filter user input
695
- ❌ Not a replacement for input validation and security controls
696
- ❌ Cannot guarantee conversation history capture (MCP protocol limitation)
697
-
698
- **Deploy HoneyMCP as part of defense-in-depth strategy, not as a standalone security control.**
699
-
700
-
701
- ### Best Practices
702
- 1. **Defense in Depth** - Use HoneyMCP alongside input filters, not as a replacement
703
- 2. **Monitor the Dashboard** - Regularly review attack patterns for both exfiltration and injection
704
- 3. **Investigate Alerts** - Each ghost tool call is a high-confidence attack signal
705
- 4. **Secure Storage** - Protect `~/.honeymcp/events/` (contains attack data)
706
-
707
- ---
708
-
709
- ## 💻 CLI Reference
710
-
711
- HoneyMCP includes a command-line tool for setup and management.
712
-
713
- ### Initialize Configuration
714
-
715
- ```bash
716
- honeymcp init [--directory DIR] [--force]
717
- ```
718
-
719
- Creates `honeymcp.yaml` and `.env.honeymcp` in the target directory.
720
-
721
- Options:
722
- - `-d, --directory` - Target directory (default: current directory)
723
- - `-f, --force` - Overwrite existing files
724
-
725
- ### Show Version
726
-
727
- ```bash
728
- honeymcp version
729
- ```
730
-
731
- ---
732
-
733
- ## 🛠️ Development
734
-
735
- ### Install from Source
736
-
737
- ```bash
738
- git clone https://github.com/barvhaim/HoneyMCP.git
739
- cd HoneyMCP
740
- uv sync
741
-
742
- # Run tests
743
- uv run pytest
744
-
745
- # Lint & format
746
- make lint
747
- make format
748
- ```
749
-
750
- ### Project Structure
751
-
752
- ```
753
- HoneyMCP/
754
- ├── src/honeymcp/
755
- │ ├── __init__.py # Main exports
756
- │ ├── cli.py # CLI (honeymcp init, version)
757
- │ ├── core/
758
- │ │ ├── middleware.py # @honeypot decorator
759
- │ │ ├── ghost_tools.py # Ghost tool catalog
760
- │ │ ├── fingerprinter.py # Attack context capture
761
- │ │ └── dynamic_ghost_tools.py# LLM-driven ghost tool generation
762
- │ ├── models/
763
- │ │ ├── events.py # AttackFingerprint model
764
- │ │ ├── ghost_tool_spec.py # GhostToolSpec definition
765
- │ │ └── config.py # Configuration
766
- │ ├── llm/
767
- │ │ ├── analyzers.py # Tool extraction and categorization
768
- │ │ ├── clients/ # LLM providers (Watsonx/OpenAI/RITS)
769
- │ │ └── prompts/ # Prompt templates
770
- │ ├── integrations/ # External integrations
771
- │ ├── storage/
772
- │ │ └── event_store.py # JSON event persistence
773
- │ └── dashboard/
774
- │ └── react_umd/ # React dashboard assets
775
- ├── examples/
776
- │ ├── demo_server.py # Static ghost tools demo
777
- │ └── demo_server_dynamic.py # Dynamic ghost tools demo
778
- ├── tests/ # Pytest suite (e2e + dynamic tools)
779
- ├── pyproject.toml # Dependencies
780
- └── README.md # This file
781
- ```
782
-
783
- ### Tests
784
-
785
- ```bash
786
- uv run pytest
787
- ```
788
-
789
- Notes:
790
- - Dynamic tool tests require LLM credentials in `.env.honeymcp` and will skip if env vars are missing.
791
-
792
577
  ## 📄 License
793
578
 
794
579
  Apache 2.0 - See [LICENSE](LICENSE) for details.
@@ -1,19 +1,19 @@
1
1
  honeymcp/__init__.py,sha256=iDVDF3MHCnR3zMdUQbeyutrJTuzzjlK-nEmdm-UqH90,881
2
- honeymcp/cli.py,sha256=EexwRLQhdC8bwFsOijhCF_ovtcI1vjE_QhqZIn5-CN8,6021
2
+ honeymcp/cli.py,sha256=JoXD3dDS8oJJ5ll70p7ufQf4JZbTWZAFC6Y8slabULA,7592
3
3
  honeymcp/cli_tool_creator.py,sha256=5rGRMcA5KbbfFz6O6ou4OgBPrBLTNna-skUhsjKp-KI,3784
4
4
  honeymcp/api/__init__.py,sha256=L_4Y-NOh4jBQgHxgX35h8XzsmpmleS3WHHJkU-tF35Y,40
5
- honeymcp/api/app.py,sha256=R39yuxCAR1tTR7YX7euS1rRF6CG2sSl0VFUweuEGzZE,7618
5
+ honeymcp/api/app.py,sha256=Wqu9l3c6iFPLXBpoxp0e51wKcr3MnPTkAV15Bgk6Z0Y,8131
6
6
  honeymcp/core/__init__.py,sha256=ja7k0fPJebDbfmGlhkpaMJa76NNaLCIpnGS7rUUdPn8,525
7
7
  honeymcp/core/catalog_updater.py,sha256=qZsKKrADjd5wWMjXGphPXHE-NNncaAT45fig81bupGY,10971
8
8
  honeymcp/core/dynamic_ghost_tools.py,sha256=GHaWZN7_XSCcXj204T4TMZyeI682WOT_JycMiM3gfp4,16731
9
- honeymcp/core/fingerprinter.py,sha256=I_GrcWiQJthzH6z5Yjwtyq18Y4NxM5zeBI0Oft5Lkrc,9768
9
+ honeymcp/core/fingerprinter.py,sha256=ZZ5gSyvWrop3R0XAR1-gU62iwyA5Dl8Dup_QKbYR2AE,9873
10
10
  honeymcp/core/ghost_tools.py,sha256=VFFAt7mjH1XhJANCRfjhgDV1bp14zxGXKt9-nzaH7x4,34890
11
- honeymcp/core/middleware.py,sha256=rMSSFI2FPDZXggcMg7UQQvGQbosR-WjWnl9P4AXXXbI,25816
11
+ honeymcp/core/middleware.py,sha256=KWL3xmXHBt8KIEKio2iF4_Eozkqwr25uvdtle-kbWDo,25904
12
12
  honeymcp/core/tool_creator.py,sha256=6Vn8c7EzTBTuqgsPLr2qPDJ6C4Al9lwb6ahdy1oFZTQ,18693
13
13
  honeymcp/dashboard/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
- honeymcp/dashboard/react_umd/app.js,sha256=zThv9cAyIiXONFK6JI51YLLBZLGBQQxwakQSldeux2c,12045
14
+ honeymcp/dashboard/react_umd/app.js,sha256=9Qee_EllLzSgWsGwRvH_oq_K250raKzR12RD2WWBbnE,13180
15
15
  honeymcp/dashboard/react_umd/index.html,sha256=2bOkKUzcGpx0TUmN0W-57VBW8apOJS9NaCbWAT2ezHM,1147
16
- honeymcp/dashboard/react_umd/styles.css,sha256=xyLr5kuYfBTs8gbMeZkZxJh4x7IiGIyu6gwut7sMqoo,10829
16
+ honeymcp/dashboard/react_umd/styles.css,sha256=koZBNTOm5exwLkzi4NWRIfPIYlBc333zjVluyMJnXWw,11245
17
17
  honeymcp/integrations/__init__.py,sha256=C-f4H12hXKa2a-taQDR1iBa6nF_S5Xt-bKpgownLI6U,67
18
18
  honeymcp/llm/__init__.py,sha256=55pJKDg15XFn7nUpOtdo5GhEDdmup5YBG-g4Lfc-5vE,256
19
19
  honeymcp/llm/analyzers.py,sha256=f_92wHNfIqLlU2KlNfPzFyrVFOwUfxU8efyrmD2x1Vg,9104
@@ -27,9 +27,9 @@ honeymcp/models/events.py,sha256=OHUjChjjjbM5GK-zEQ-o2njDY-zJeGLvLdjWhIJz-OE,227
27
27
  honeymcp/models/ghost_tool_spec.py,sha256=KM_M-e4Ys_jr3rUfREDiZ-oa331KWcyt5B7zMD7MeZU,917
28
28
  honeymcp/models/protection_mode.py,sha256=mo1_EnBeIOzyHxgEpReZx4lMJ6m__36edUWDJMzuRak,523
29
29
  honeymcp/storage/__init__.py,sha256=seOZHWpojp1fU65OFuLcNqJaBihrlNyUPeq9BDwAEVI,207
30
- honeymcp/storage/event_store.py,sha256=mneqVxkTi0bVbTOdxhIYBCjia0Wv59A6FudNRdgvphE,5431
31
- honeymcp-0.1.3.dist-info/METADATA,sha256=_nj55_KPvnDG4jYct7aiPZ9ieKLw-7DdRPgzW8jRUJ0,28671
32
- honeymcp-0.1.3.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
33
- honeymcp-0.1.3.dist-info/entry_points.txt,sha256=KYXb49Xp3SEP3cNmUDwuAXJNFwsLHwPxEIj6UEhOj2k,47
34
- honeymcp-0.1.3.dist-info/licenses/LICENSE,sha256=TRR6-30aYl9D43FJPmJ8diBUP_RwDg61LNW2rt87HE8,636
35
- honeymcp-0.1.3.dist-info/RECORD,,
30
+ honeymcp/storage/event_store.py,sha256=RYcgI-HCoX6CGNnEB_WwShLBxyg1q9KhvH2qmSIXhwo,6337
31
+ honeymcp-0.1.4.dist-info/METADATA,sha256=SOdoUS1iI3fpBKlmgB5VVG1r0dmzMf__FuesL6aDVh0,19295
32
+ honeymcp-0.1.4.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
33
+ honeymcp-0.1.4.dist-info/entry_points.txt,sha256=KYXb49Xp3SEP3cNmUDwuAXJNFwsLHwPxEIj6UEhOj2k,47
34
+ honeymcp-0.1.4.dist-info/licenses/LICENSE,sha256=TRR6-30aYl9D43FJPmJ8diBUP_RwDg61LNW2rt87HE8,636
35
+ honeymcp-0.1.4.dist-info/RECORD,,