-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathserver.py
More file actions
1736 lines (1512 loc) · 68.8 KB
/
server.py
File metadata and controls
1736 lines (1512 loc) · 68.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# server.py
"""
EU AI Act Article 50 Compliance MCP Server
This MCP server provides tools and resources for EU AI Act Article 50 compliance,
including transparency obligations for AI systems.
"""
import os
import json
from typing import Dict, Any, List
from mcp.server.fastmcp import FastMCP
from dotenv import load_dotenv
# Load environment variables (if needed for future extensions)
load_dotenv()
# Create an MCP server with a name
mcp = FastMCP("EU_AI_ACT_MCP")
# ============================================================================
# RESOURCES - Data files that agents can read
# ============================================================================
@mcp.resource("disclosure-templates://ai-interaction-and-emotion")
def get_disclosure_templates() -> str:
"""
Provides pre-written disclosure text templates for EU AI Act Article 50 compliance.
Contains:
- AI interaction disclosures (Article 50(1))
- Emotion recognition disclosures (Article 50(3))
Available in multiple languages: en, es, fr, de, it
"""
template_path = os.path.join(os.path.dirname(__file__), "resources", "disclosure_templates.json")
with open(template_path, 'r', encoding='utf-8') as f:
return f.read()
@mcp.resource("deepfake-labels://content-labeling")
def get_deepfake_labels() -> str:
"""
Provides pre-written deepfake and AI-generated content labels for EU AI Act Article 50(4) compliance.
Contains labels for:
- Text content (news articles, public interest content)
- Image deepfakes
- Video deepfakes
- Audio deepfakes
Available in multiple languages: en, es, fr, de
"""
labels_path = os.path.join(os.path.dirname(__file__), "resources", "deepfake_labels.json")
with open(labels_path, 'r', encoding='utf-8') as f:
return f.read()
@mcp.resource("article50-rules://official-text")
def get_article50_rules() -> str:
"""
Provides the official EU AI Act Article 50 rules and requirements.
Contains:
- Complete Article 50 obligations (paragraphs 50(1), 50(2), 50(3), 50(4))
- Provider obligations (AI interaction, content watermarking)
- Deployer obligations (emotion recognition, deepfake labeling)
- Exceptions and exemptions
- Compliance deadlines
- Penalty information
- Key definitions
Use this resource to understand which obligations apply to your AI system.
"""
rules_path = os.path.join(os.path.dirname(__file__), "resources", "article50_rules.json")
with open(rules_path, 'r', encoding='utf-8') as f:
return f.read()
@mcp.resource("watermark-config://technical-standards")
def get_watermark_config() -> str:
"""
Provides watermarking configuration and technical standards for Article 50(2).
Contains:
- C2PA 2.1 specifications (Coalition for Content Provenance and Authenticity)
- IPTC metadata standards
- Content type configurations (image, video, audio, text)
- Embedding settings and parameters
- Verification methods
- Implementation guide
Use this resource to understand how to properly watermark AI-generated content
with machine-readable, detectable metadata that complies with Article 50(2).
"""
config_path = os.path.join(os.path.dirname(__file__), "resources", "watermark_config.json")
with open(config_path, 'r', encoding='utf-8') as f:
return f.read()
# ============================================================================
# TOOLS - Article 50 Compliance Tools
# ============================================================================
@mcp.tool()
def get_ai_interaction_disclosure(language: str = "en", style: str = "simple") -> Dict[str, Any]:
"""
Get AI interaction disclosure text for EU AI Act Article 50(1) compliance.
This tool provides pre-written disclosure text that MUST be shown to users
when they interact with an AI system (chatbots, voice assistants, etc.).
Args:
language: Language code (en, es, fr, de, it). Default: "en"
style: Disclosure style (simple, detailed, voice). Default: "simple"
Returns:
Dictionary containing the disclosure text and metadata
Example:
get_ai_interaction_disclosure(language="en", style="simple")
Returns: {"disclosure": "You are chatting with an AI assistant.", ...}
"""
template_path = os.path.join(os.path.dirname(__file__), "resources", "disclosure_templates.json")
with open(template_path, 'r', encoding='utf-8') as f:
templates = json.load(f)
# Get the requested disclosure
try:
disclosure_text = templates["ai_interaction"][language][style]
except KeyError:
return {
"error": f"Disclosure not found for language '{language}' and style '{style}'",
"available_languages": list(templates["ai_interaction"].keys()),
"available_styles": ["simple", "detailed", "voice"]
}
return {
"article": "50(1)",
"obligation": "AI Interaction Transparency",
"language": language,
"style": style,
"disclosure": disclosure_text,
"usage": "Display this text to users before or during AI interaction",
"compliance_deadline": "2026-08-02"
}
@mcp.tool()
def get_emotion_recognition_disclosure(language: str = "en", style: str = "simple") -> Dict[str, Any]:
"""
Get emotion recognition disclosure text for EU AI Act Article 50(3) compliance.
This tool provides pre-written disclosure text that MUST be shown to users
when an AI system uses emotion recognition technology.
Args:
language: Language code (en, es, fr, de, it). Default: "en"
style: Disclosure style (simple, detailed, privacy_notice). Default: "simple"
Returns:
Dictionary containing the disclosure text and metadata
Example:
get_emotion_recognition_disclosure(language="en", style="detailed")
"""
template_path = os.path.join(os.path.dirname(__file__), "resources", "disclosure_templates.json")
with open(template_path, 'r', encoding='utf-8') as f:
templates = json.load(f)
# Get the requested disclosure
try:
disclosure_text = templates["emotion_recognition"][language][style]
except KeyError:
return {
"error": f"Disclosure not found for language '{language}' and style '{style}'",
"available_languages": list(templates["emotion_recognition"].keys()),
"available_styles": ["simple", "detailed", "privacy_notice"]
}
return {
"article": "50(3)",
"obligation": "Emotion Recognition Transparency",
"language": language,
"style": style,
"disclosure": disclosure_text,
"usage": "Display this text to users before activating emotion recognition",
"gdpr_compliance": "Ensure user consent is obtained",
"compliance_deadline": "2026-08-02"
}
@mcp.tool()
def get_deepfake_label_templates(language: str = "en") -> Dict[str, Any]:
"""
Get all available deepfake and AI-generated content labels.
This tool returns the complete set of labels available for different content types.
Use this to see what labels are available for images, videos, audio, and text.
Args:
language: Language code (en, es, fr, de). Default: "en"
Returns:
Dictionary containing all available labels organized by content type
Example:
get_deepfake_label_templates(language="en")
Returns all labels for English
"""
labels_path = os.path.join(os.path.dirname(__file__), "resources", "deepfake_labels.json")
with open(labels_path, 'r', encoding='utf-8') as f:
all_labels = json.load(f)
# Filter by language if available
result = {
"language": language,
"content_types": {}
}
for content_type in ["text", "image", "video", "audio"]:
if content_type in all_labels:
if language in all_labels[content_type]:
result["content_types"][content_type] = all_labels[content_type][language]
else:
result["content_types"][content_type] = {
"error": f"Language '{language}' not available for {content_type}",
"available_languages": list(all_labels[content_type].keys())
}
result["article"] = "50(2) and 50(4)"
result["purpose"] = "Labels for AI-generated and manipulated content"
result["available_languages"] = ["en", "es", "fr", "de"]
return result
@mcp.tool()
def label_news_text(
text_content: str,
has_human_editor: bool = False,
editor_name: str = "",
language: str = "en"
) -> Dict[str, Any]:
"""
Add AI-generated content disclosure to news articles and public interest text.
This tool implements EU AI Act Article 50(4) compliance for AI-generated text
published as news, journalism, or public interest content.
Args:
text_content: The AI-generated or AI-assisted text content
has_human_editor: Whether a human editor reviewed the content (exemption qualifier)
editor_name: Name of the human editor (if applicable)
language: Language code (en, es, fr, de). Default: "en"
Returns:
Dictionary containing the labeled text with disclosure and compliance info
Example:
label_news_text(
text_content="AI generated article...",
has_human_editor=True,
editor_name="Jane Doe",
language="en"
)
"""
labels_path = os.path.join(os.path.dirname(__file__), "resources", "deepfake_labels.json")
with open(labels_path, 'r', encoding='utf-8') as f:
labels = json.load(f)
# Get the appropriate label based on editor status
try:
if has_human_editor and editor_name:
label_template = labels["text"][language]["news"]
disclosure = label_template.replace("{editor}", editor_name)
elif has_human_editor:
label_template = labels["text"][language]["news"]
disclosure = label_template.replace("{editor}", "editorial team")
else:
disclosure = labels["text"][language]["news_no_editor"]
except KeyError:
return {
"error": f"Labels not found for language '{language}'",
"available_languages": list(labels["text"].keys())
}
# Add disclosure at the beginning of the text
labeled_text = f"[{disclosure}]\n\n{text_content}"
# Determine if exemption applies
exemption_applies = has_human_editor
exemption_reason = "Human editorial oversight present" if has_human_editor else "No human editorial oversight"
return {
"article": "50(4)",
"obligation": "AI-Generated Content Labeling (Text/News)",
"language": language,
"labeled_text": labeled_text,
"disclosure": disclosure,
"original_length": len(text_content),
"labeled_length": len(labeled_text),
"has_human_editor": has_human_editor,
"exemption_applies": exemption_applies,
"exemption_reason": exemption_reason,
"compliance_deadline": "2026-08-02",
"usage": "Publish the labeled_text instead of the original text"
}
@mcp.tool()
def watermark_text(
text_content: str,
generator: str = "AI",
format_type: str = "plain"
) -> Dict[str, Any]:
"""
Add metadata watermark to AI-generated text for EU AI Act Article 50(2) compliance.
This tool adds machine-readable metadata to AI-generated text content,
marking it as artificially generated. This is required for provider compliance
with Article 50(2) for text content generation systems.
Args:
text_content: The AI-generated text to watermark
generator: Name of the AI system that generated it (e.g., "GPT-4", "Claude", "Custom AI")
format_type: Output format (plain, markdown, html). Default: "plain"
Returns:
Dictionary containing the watermarked text with embedded metadata
Example:
watermark_text(
text_content="This is AI-generated content...",
generator="GPT-4",
format_type="markdown"
)
"""
import hashlib
from datetime import datetime, timezone
# Generate content hash for integrity verification
content_hash = hashlib.sha256(text_content.encode('utf-8')).hexdigest()[:16]
# Create timestamp
timestamp = datetime.now(timezone.utc).isoformat()
# Create metadata
metadata = {
"ai_generated": True,
"generator": generator,
"timestamp": timestamp,
"content_hash": content_hash,
"compliance": "EU AI Act Article 50(2)",
"watermark_version": "1.0"
}
# Format the watermarked text based on format type
if format_type == "html":
watermarked_text = f'''<!-- AI-Generated Content Metadata
{json.dumps(metadata, indent=2)}
-->
{text_content}'''
elif format_type == "markdown":
watermarked_text = f'''<!-- AI Watermark: {json.dumps(metadata)} -->
{text_content}'''
else: # plain text
metadata_str = json.dumps(metadata, separators=(',', ':'))
watermarked_text = f"[AI-WATERMARK:{metadata_str}]\n\n{text_content}"
return {
"article": "50(2)",
"obligation": "Content Watermarking (Text)",
"watermarked_text": watermarked_text,
"metadata": metadata,
"original_length": len(text_content),
"watermarked_length": len(watermarked_text),
"format": format_type,
"machine_readable": True,
"detectable": True,
"compliance_deadline": "2026-08-02",
"usage": "Use watermarked_text instead of original. Metadata is machine-readable.",
"verification": f"Content hash: {content_hash}"
}
@mcp.tool()
def label_image_deepfake(
image_description: str,
is_artistic_work: bool = False,
is_satirical: bool = False,
language: str = "en"
) -> Dict[str, Any]:
"""
Generate deepfake label for AI-generated or manipulated images per Article 50(4).
This tool provides the appropriate disclosure text and guidance for labeling
images that have been artificially generated or manipulated. The label must
be prominent, clear, and distinguishable.
Args:
image_description: Brief description of the image for context
is_artistic_work: Whether this is artistic/creative work (may qualify for exemption)
is_satirical: Whether this is parody/satire (may qualify for exemption)
language: Language code (en, es, fr, de). Default: "en"
Returns:
Dictionary with label text, placement guidance, and compliance info
Example:
label_image_deepfake(
image_description="AI-generated portrait of a person",
is_artistic_work=False,
language="en"
)
"""
labels_path = os.path.join(os.path.dirname(__file__), "resources", "deepfake_labels.json")
with open(labels_path, 'r', encoding='utf-8') as f:
labels = json.load(f)
# Get the appropriate label based on artistic status
try:
if is_artistic_work or is_satirical:
label_text = labels["image"][language]["artistic"]
exemption_applies = True
exemption_reason = "Artistic work or satire - modified disclosure"
else:
label_text = labels["image"][language]["standard"]
exemption_applies = False
exemption_reason = "Standard disclosure required"
except KeyError:
return {
"error": f"Labels not found for language '{language}'",
"available_languages": list(labels.get("image", {}).keys())
}
# Generate placement guidance
placement_options = [
"Top-left corner overlay",
"Bottom banner overlay",
"Visible watermark across image",
"Caption below image"
]
return {
"article": "50(4)",
"obligation": "Deepfake Labeling (Image)",
"applies_to": "deployer",
"image_description": image_description,
"label_text": label_text,
"language": language,
"placement_options": placement_options,
"recommended_placement": "Top-left corner overlay with semi-transparent background",
"visibility_requirement": "Prominent and clearly distinguishable",
"label_persistence": "Must not be easily removable",
"is_artistic_work": is_artistic_work,
"is_satirical": is_satirical,
"exemption_applies": exemption_applies,
"exemption_reason": exemption_reason,
"compliance_deadline": "2026-08-02",
"implementation_notes": [
"Label must be visible without zooming or special tools",
"Text size must be legible (minimum 12pt or 5% of image height)",
"Background contrast must ensure readability",
"Label should persist in downloaded/shared versions"
],
"usage": "Add this label text to the image using one of the placement options"
}
@mcp.tool()
def label_video_deepfake(
video_description: str,
is_artistic_work: bool = False,
is_satirical: bool = False,
language: str = "en"
) -> Dict[str, Any]:
"""
Generate deepfake label for AI-generated or manipulated videos per Article 50(4).
This tool provides the appropriate disclosure text and guidance for labeling
videos that have been artificially generated or manipulated. The label must
be prominent, clear, and distinguishable throughout the video.
Args:
video_description: Brief description of the video for context
is_artistic_work: Whether this is artistic/creative work (may qualify for exemption)
is_satirical: Whether this is parody/satire (may qualify for exemption)
language: Language code (en, es, fr, de). Default: "en"
Returns:
Dictionary with label text, placement guidance, and compliance info
Example:
label_video_deepfake(
video_description="AI-generated video of a speech",
is_artistic_work=False,
language="en"
)
"""
labels_path = os.path.join(os.path.dirname(__file__), "resources", "deepfake_labels.json")
with open(labels_path, 'r', encoding='utf-8') as f:
labels = json.load(f)
# Get the appropriate label based on artistic status
try:
if is_artistic_work or is_satirical:
label_text = labels["video"][language]["artistic"]
exemption_applies = True
exemption_reason = "Artistic work or satire - modified disclosure"
else:
label_text = labels["video"][language]["standard"]
exemption_applies = False
exemption_reason = "Standard disclosure required"
except KeyError:
return {
"error": f"Labels not found for language '{language}'",
"available_languages": list(labels.get("video", {}).keys())
}
# Generate placement guidance for video
placement_options = [
"Persistent overlay in corner throughout video",
"Opening title card (3-5 seconds)",
"Closing credit with disclosure",
"Intermittent overlay every 30 seconds"
]
return {
"article": "50(4)",
"obligation": "Deepfake Labeling (Video)",
"applies_to": "deployer",
"video_description": video_description,
"label_text": label_text,
"language": language,
"placement_options": placement_options,
"recommended_placement": "Persistent semi-transparent overlay in top-left corner",
"visibility_requirement": "Clearly visible and distinguishable throughout playback",
"label_persistence": "Must persist in all playback formats and cannot be easily removed",
"timing_guidance": "If using title card, display for minimum 3 seconds at start",
"is_artistic_work": is_artistic_work,
"is_satirical": is_satirical,
"exemption_applies": exemption_applies,
"exemption_reason": exemption_reason,
"compliance_deadline": "2026-08-02",
"implementation_notes": [
"Label must be visible at standard playback resolution",
"Text size must be legible (minimum 5% of frame height)",
"Use high contrast background for readability",
"Label must persist through video editing and re-encoding",
"Consider accessibility: include spoken disclosure for audio description"
],
"usage": "Add this label to the video using one of the placement options"
}
@mcp.tool()
def label_audio_deepfake(
audio_description: str,
is_artistic_work: bool = False,
language: str = "en"
) -> Dict[str, Any]:
"""
Generate deepfake label for AI-generated or manipulated audio per Article 50(4).
This tool provides disclosure text for audio content that has been artificially
generated or manipulated. For audio, disclosure can be spoken, written in
accompanying materials, or both.
Args:
audio_description: Brief description of the audio for context
is_artistic_work: Whether this is artistic/creative work (may qualify for exemption)
language: Language code (en, es, fr, de). Default: "en"
Returns:
Dictionary with label text (written and spoken), placement guidance, and compliance info
Example:
label_audio_deepfake(
audio_description="AI-generated voice recording",
is_artistic_work=False,
language="en"
)
"""
labels_path = os.path.join(os.path.dirname(__file__), "resources", "deepfake_labels.json")
with open(labels_path, 'r', encoding='utf-8') as f:
labels = json.load(f)
# Get labels for audio
try:
written_label = labels["audio"][language]["standard"]
spoken_label = labels["audio"][language]["spoken"]
exemption_applies = is_artistic_work
exemption_reason = "Artistic work - modified disclosure may apply" if is_artistic_work else "Standard disclosure required"
except KeyError:
return {
"error": f"Labels not found for language '{language}'",
"available_languages": list(labels.get("audio", {}).keys())
}
# Disclosure methods for audio
disclosure_methods = [
"Spoken announcement at beginning of audio",
"Written disclosure in audio player interface",
"Metadata embedded in audio file",
"Text description accompanying audio"
]
return {
"article": "50(4)",
"obligation": "Deepfake Labeling (Audio)",
"applies_to": "deployer",
"audio_description": audio_description,
"written_label": written_label,
"spoken_label": spoken_label,
"language": language,
"disclosure_methods": disclosure_methods,
"recommended_method": "Spoken announcement at beginning + written metadata",
"spoken_disclosure_timing": "Beginning of audio (first 3 seconds)",
"is_artistic_work": is_artistic_work,
"exemption_applies": exemption_applies,
"exemption_reason": exemption_reason,
"compliance_deadline": "2026-08-02",
"implementation_notes": [
"Spoken disclosure should be clear and at normal speech volume",
"Written disclosure must accompany audio in player/platform",
"Embed disclosure in audio file metadata (ID3 tags, etc.)",
"Consider accessibility: provide written version for deaf users",
"Disclosure should be in same language as primary audio content"
],
"usage": "Add spoken disclosure at audio start and include written label in metadata/description"
}
@mcp.tool()
def watermark_image(
image_description: str,
generator: str = "AI",
format_type: str = "png"
) -> Dict[str, Any]:
"""
Generate watermarking metadata for AI-generated images per Article 50(2).
This tool provides C2PA-compliant metadata and instructions for watermarking
AI-generated images. The watermark must be machine-readable and detectable.
Args:
image_description: Brief description of the image
generator: Name of AI system that generated it (e.g., "DALL-E", "Midjourney")
format_type: Image format (png, jpg, webp). Default: "png"
Returns:
Dictionary with watermarking metadata, instructions, and compliance info
Example:
watermark_image(
image_description="AI-generated landscape",
generator="DALL-E",
format_type="png"
)
"""
from datetime import datetime, timezone
import hashlib
timestamp = datetime.now(timezone.utc).isoformat()
content_hash = hashlib.sha256(image_description.encode()).hexdigest()[:16]
# C2PA metadata structure
c2pa_metadata = {
"claim_generator": "EU AI Act Compliance MCP Server",
"claim_timestamp": timestamp,
"assertions": {
"c2pa.actions": "ai_generated",
"stds.schema-org.CreativeWork": {
"creator": generator,
"dateCreated": timestamp,
"description": image_description,
"ai_generated": True
}
},
"signature": "ES256",
"hash_algorithm": "SHA-256",
"content_hash": content_hash
}
# IPTC metadata
iptc_metadata = {
"Digital Source Type": "trainedAlgorithmicMedia",
"Credit": f"Generated by {generator}",
"Creator": generator,
"Date Created": timestamp,
"Copyright Notice": "AI-generated content subject to EU AI Act Article 50(2)"
}
return {
"article": "50(2)",
"obligation": "Content Watermarking (Image)",
"applies_to": "provider",
"image_description": image_description,
"generator": generator,
"format": format_type,
"c2pa_metadata": c2pa_metadata,
"iptc_metadata": iptc_metadata,
"watermark_standard": "C2PA 2.1",
"machine_readable": True,
"detectable": True,
"compliance_deadline": "2026-08-02",
"implementation_instructions": [
f"1. Use C2PA library to embed metadata in {format_type} file",
"2. Add IPTC metadata as fallback",
"3. Ensure watermark survives compression and resizing",
"4. Verify watermark using C2PA verification tools",
"5. Store watermarked version separately from original"
],
"verification_url": "https://verify.contentauthenticity.org/",
"usage": "Use provided metadata to watermark the image file using C2PA-compliant tools"
}
@mcp.tool()
def watermark_video(
video_description: str,
generator: str = "AI",
format_type: str = "mp4"
) -> Dict[str, Any]:
"""
Generate watermarking metadata for AI-generated videos per Article 50(2).
This tool provides C2PA-compliant metadata and instructions for watermarking
AI-generated videos. The watermark must be machine-readable and detectable.
Args:
video_description: Brief description of the video
generator: Name of AI system that generated it
format_type: Video format (mp4, webm, mov). Default: "mp4"
Returns:
Dictionary with watermarking metadata, instructions, and compliance info
"""
from datetime import datetime, timezone
import hashlib
timestamp = datetime.now(timezone.utc).isoformat()
content_hash = hashlib.sha256(video_description.encode()).hexdigest()[:16]
c2pa_metadata = {
"claim_generator": "EU AI Act Compliance MCP Server",
"claim_timestamp": timestamp,
"assertions": {
"c2pa.actions": "ai_generated",
"stds.schema-org.VideoObject": {
"creator": generator,
"dateCreated": timestamp,
"description": video_description,
"ai_generated": True
}
},
"signature": "ES256",
"hash_algorithm": "SHA-256",
"content_hash": content_hash
}
return {
"article": "50(2)",
"obligation": "Content Watermarking (Video)",
"applies_to": "provider",
"video_description": video_description,
"generator": generator,
"format": format_type,
"c2pa_metadata": c2pa_metadata,
"watermark_standard": "C2PA 2.1",
"watermark_method": "Frame-level embedding",
"machine_readable": True,
"detectable": True,
"compliance_deadline": "2026-08-02",
"implementation_instructions": [
f"1. Use C2PA video library to embed metadata in {format_type} file",
"2. Apply watermark at frame level for persistence",
"3. Embed metadata in video container and frames",
"4. Ensure watermark survives re-encoding",
"5. Test with multiple video players"
],
"verification_url": "https://verify.contentauthenticity.org/",
"usage": "Use provided metadata to watermark the video file using C2PA-compliant tools"
}
@mcp.tool()
def watermark_audio(
audio_description: str,
generator: str = "AI",
format_type: str = "mp3"
) -> Dict[str, Any]:
"""
Generate watermarking metadata for AI-generated audio per Article 50(2).
This tool provides metadata and instructions for watermarking AI-generated audio.
Audio watermarks use fingerprinting and metadata embedding.
Args:
audio_description: Brief description of the audio
generator: Name of AI system that generated it
format_type: Audio format (mp3, wav, opus). Default: "mp3"
Returns:
Dictionary with watermarking metadata, instructions, and compliance info
"""
from datetime import datetime, timezone
import hashlib
timestamp = datetime.now(timezone.utc).isoformat()
content_hash = hashlib.sha256(audio_description.encode()).hexdigest()[:16]
c2pa_metadata = {
"claim_generator": "EU AI Act Compliance MCP Server",
"claim_timestamp": timestamp,
"assertions": {
"c2pa.actions": "ai_generated",
"stds.schema-org.AudioObject": {
"creator": generator,
"dateCreated": timestamp,
"description": audio_description,
"ai_generated": True
}
},
"content_hash": content_hash
}
# Audio-specific metadata
audio_metadata = {
"ID3_tags": {
"TIT2": audio_description,
"TPE1": generator,
"COMM": "AI-generated audio - EU AI Act Article 50(2)",
"TDRC": timestamp
}
}
return {
"article": "50(2)",
"obligation": "Content Watermarking (Audio)",
"applies_to": "provider",
"audio_description": audio_description,
"generator": generator,
"format": format_type,
"c2pa_metadata": c2pa_metadata,
"audio_metadata": audio_metadata,
"watermark_method": "Spectral embedding + ID3 tags",
"machine_readable": True,
"detectable": True,
"inaudible": True,
"compliance_deadline": "2026-08-02",
"implementation_instructions": [
f"1. Embed C2PA metadata in {format_type} file",
"2. Add ID3 tags for MP3 or equivalent for other formats",
"3. Apply inaudible spectral watermark (18-20kHz range)",
"4. Ensure watermark survives format conversion",
"5. Test detectability after compression"
],
"usage": "Use provided metadata to watermark the audio file"
}
# ============================================================================
# RISK CLASSIFICATION TOOLS - Articles 5, 6, and Annex III
# ============================================================================
@mcp.tool()
def classify_ai_system_risk(
system_description: str,
use_case: str,
biometric_data: bool = False,
critical_infrastructure: bool = False,
education: bool = False,
law_enforcement: bool = False,
predicts_criminal_behavior: bool = False,
social_scoring: bool = False,
emotion_detection_workplace: bool = False,
generates_content: bool = False,
interacts_with_users: bool = False
) -> Dict[str, Any]:
"""
Determine AI system risk level per EU AI Act classification framework.
Classifies system as: PROHIBITED, HIGH-RISK, LIMITED-RISK, or MINIMAL-RISK
based on Articles 5, 6, and 50.
Args:
system_description: Description of the AI system
use_case: Primary use case (e.g., "employment", "healthcare", "chatbot")
biometric_data: Uses biometric identification/categorization
critical_infrastructure: Used in critical infrastructure
education: Used in education/vocational training
law_enforcement: Used for law enforcement
predicts_criminal_behavior: Predicts criminal behavior from profiling
social_scoring: Performs social scoring
emotion_detection_workplace: Detects emotions in workplace/education
generates_content: Generates synthetic content
interacts_with_users: Interacts with natural persons
Returns:
Risk classification with applicable obligations and deadlines
"""
# Step 1: Check Article 5 - PROHIBITED practices
if social_scoring:
return {
"risk_level": "PROHIBITED",
"article": "Article 5(1)(c)",
"reason": "Social scoring by public authorities or on their behalf",
"system_description": system_description,
"compliance_action": "MUST NOT deploy - System is prohibited",
"penalties": "Up to €35 million or 7% of global annual turnover (whichever is higher)",
"deadline": "Immediate - Already in effect",
"recommendation": "Discontinue development or deployment immediately"
}
if emotion_detection_workplace:
return {
"risk_level": "PROHIBITED",
"article": "Article 5(1)(f)",
"reason": "Emotion recognition in workplace or education (except medical/safety)",
"system_description": system_description,
"compliance_action": "MUST NOT deploy - System is prohibited",
"exception": "Allowed only for medical or safety reasons",
"penalties": "Up to €35 million or 7% of global annual turnover (whichever is higher)",
"deadline": "Immediate - Already in effect",
"recommendation": "Remove emotion detection or limit to medical/safety contexts"
}
if predicts_criminal_behavior:
return {
"risk_level": "PROHIBITED",
"article": "Article 5(1)(d)",
"reason": "Risk assessment predicting criminal offenses based on profiling",
"system_description": system_description,
"compliance_action": "MUST NOT deploy - System is prohibited",
"penalties": "Up to €35 million or 7% of global annual turnover (whichever is higher)",
"deadline": "Immediate - Already in effect",
"recommendation": "Discontinue predictive profiling features"
}
# Step 2: Check Article 6 + Annex III - HIGH-RISK systems
high_risk_checks = []
if biometric_data:
high_risk_checks.append({
"reason": "Biometric identification or categorization",
"annex_point": "Annex III point 1",
"article_ref": "Article 6(2)"
})
if use_case.lower() in ["employment", "hiring", "hr", "recruitment"]:
high_risk_checks.append({
"reason": "AI system for employment, recruitment, or HR decisions",
"annex_point": "Annex III point 4(a)",
"article_ref": "Article 6(2)"
})
if education:
high_risk_checks.append({
"reason": "AI system for education or vocational training",
"annex_point": "Annex III point 3",
"article_ref": "Article 6(2)"
})
if law_enforcement:
high_risk_checks.append({
"reason": "AI system for law enforcement",
"annex_point": "Annex III point 6",
"article_ref": "Article 6(2)"
})
if critical_infrastructure:
high_risk_checks.append({
"reason": "AI system for critical infrastructure",
"annex_point": "Annex III point 2",
"article_ref": "Article 6(2)"
})
if high_risk_checks:
return {
"risk_level": "HIGH-RISK",
"article": high_risk_checks[0]["article_ref"],
"annex_reference": high_risk_checks[0]["annex_point"],
"reason": high_risk_checks[0]["reason"],
"system_description": system_description,
"all_high_risk_factors": [check["reason"] for check in high_risk_checks],
"applicable_obligations": [
"Risk management system (Article 9)",