Entity Relationships, Branded Types, and EP API Data Structures
Comprehensive data model documentation for parliamentary intelligence data
π Document Owner: Hack23 | π Version: 1.1 | π Last Updated: 2026-03-19 (UTC) π Review Cycle: Quarterly | β° Next Review: 2026-06-19 π·οΈ Classification: Public (Open Source MCP Server) β ISMS Compliance: ISO 27001 (A.5.1, A.8.1, A.14.2), NIST CSF 2.0 (ID.AM, PR.DS), CIS Controls v8.1 (2.1, 16.1)
| Document | Current | Future | Description |
|---|---|---|---|
| Architecture | ARCHITECTURE.md | FUTURE_ARCHITECTURE.md | C4 model, containers, components, ADRs |
| Security Architecture | SECURITY_ARCHITECTURE.md | FUTURE_SECURITY_ARCHITECTURE.md | Security controls, threat model |
| Data Model | DATA_MODEL.md | FUTURE_DATA_MODEL.md | Entity relationships, branded types |
| Flowchart | FLOWCHART.md | FUTURE_FLOWCHART.md | Business process flows |
| State Diagram | STATEDIAGRAM.md | FUTURE_STATEDIAGRAM.md | System state transitions |
| Mind Map | MINDMAP.md | FUTURE_MINDMAP.md | System concepts and relationships |
| SWOT Analysis | SWOT.md | FUTURE_SWOT.md | Strategic positioning |
| Threat Model | THREAT_MODEL.md | FUTURE_THREAT_MODEL.md | STRIDE, MITRE ATT&CK, attack trees |
| CRA Assessment | CRA-ASSESSMENT.md | β | EU Cyber Resilience Act conformity |
The EP MCP Server's data model reflects the structure of the European Parliament Open Data Portal API v2 (JSON-LD format). All entities are defined as TypeScript interfaces with corresponding Zod schemas for runtime validation. Branded types enforce semantic correctness for EP-specific identifiers.
https://data.europarl.europa.eu/api/v2/erDiagram
MEP {
MEP_ID id PK
string identifier
string label
CountryCode country
string politicalGroup
DateString mandateStart
DateString mandateEnd
string email
string status
}
POLITICAL_GROUP {
string id PK
string label
string abbreviation
int memberCount
}
COMMITTEE {
string id PK
string label
string abbreviation
string type
}
PROCEDURE {
ProcedureID id PK
string title
string type
string stage
DateString startDate
string leadCommittee
}
PLENARY_SESSION {
string id PK
DateString date
string location
string status
int documentCount
}
VOTE {
string id PK
string procedureRef
string result
int votesFor
int votesAgainst
int abstentions
DateString date
}
DOCUMENT {
string id PK
string title
string type
string procedureRef
DateString adoptedDate
string url
}
SPEECH {
string id PK
MEP_ID mepRef
string sessionRef
DateString date
string language
int durationSeconds
}
QUESTION {
string id PK
MEP_ID authorRef
string type
string subject
DateString submittedDate
string status
}
MEP }|--|| POLITICAL_GROUP : "belongs to"
MEP }|--|{ COMMITTEE : "member of"
MEP ||--|{ SPEECH : "delivered"
MEP ||--|{ QUESTION : "submitted"
PROCEDURE ||--|{ DOCUMENT : "produces"
PROCEDURE ||--|{ VOTE : "subject of"
PLENARY_SESSION ||--|{ VOTE : "held in"
PLENARY_SESSION ||--|{ SPEECH : "delivered in"
COMMITTEE ||--|{ PROCEDURE : "responsible for"
erDiagram
MEP {
MEP_ID id PK
string identifier "EP identifier e.g. ID12345"
string label "Full name"
CountryCode country "ISO 3166-1 alpha-2"
string nationalParty "Home party name"
string politicalGroup "EPP, S-D, Renew, etc."
string politicalGroupAbbr "Short code"
DateString mandateStart "Current mandate start"
DateString mandateEnd "Mandate end (null if current)"
string officialEmail "EP institutional email"
string status "current, incoming, outgoing, homonym"
string[] committeeRoles "Committee memberships"
string photoUrl "Official EP photo URL"
}
MEP_DECLARATION {
string id PK
MEP_ID mepRef FK
string type "financial, interest, activity"
DateString submittedDate
string documentUrl
}
MEP_ATTENDANCE {
string id PK
MEP_ID mepRef FK
string sessionRef FK
string status "present, absent, excused"
DateString date
}
MEP ||--|{ MEP_DECLARATION : "has"
MEP ||--|{ MEP_ATTENDANCE : "tracked in"
erDiagram
PROCEDURE {
ProcedureID id PK "Format: YYYY-NNNN-TYPE"
string title
string type "COD, CNS, INI, etc."
string stage "committee, plenary, trilogue, etc."
string status "ongoing, completed, withdrawn"
DateString startDate
DateString adoptedDate
string leadCommittee
string rapporteur
string[] coRapporteurs
string legalBasis
string[] relatedDocuments
}
PROCEDURE_EVENT {
string id PK
ProcedureID procedureRef FK
string eventType "committee_vote, plenary_vote, etc."
DateString date
string description
string documentRef
}
ADOPTED_TEXT {
string id PK
ProcedureID procedureRef FK
string title
DateString adoptedDate
string url
string documentType
}
PROCEDURE ||--|{ PROCEDURE_EVENT : "has events"
PROCEDURE ||--|{ ADOPTED_TEXT : "produces"
erDiagram
PLENARY_SESSION {
string id PK
DateString date
string location "Strasbourg or Brussels"
string status "scheduled, completed, cancelled"
string[] agendaItems
int attendanceCount
}
COMMITTEE_MEETING {
string id PK
string committeeId FK
DateString date
string location
string status
string[] agendaItems
}
MEETING_ACTIVITY {
string id PK
string meetingRef FK
string activityType
string description
string documentRef
string outcome
}
MEETING_DECISION {
string id PK
string meetingRef FK
string decisionType
string text
DateString date
string[] votes
}
PLENARY_SESSION ||--|{ MEETING_ACTIVITY : "contains"
COMMITTEE_MEETING ||--|{ MEETING_ACTIVITY : "contains"
PLENARY_SESSION ||--|{ MEETING_DECISION : "produces"
COMMITTEE_MEETING ||--|{ MEETING_DECISION : "produces"
erDiagram
DOCUMENT {
string id PK
string title
string type "A-item, B-item, report, amendment"
string procedureRef
DateString createdDate
DateString adoptedDate
string[] languages
string url
string status "draft, adopted, rejected"
}
EXTERNAL_DOCUMENT {
string id PK
string title
string source "Commission, Council, etc."
string documentNumber
DateString date
string url
string[] relatedProcedures
}
PLENARY_DOCUMENT {
string id PK
string sessionRef FK
string documentRef FK
string agendaPosition
string discussionType
}
COMMITTEE_DOCUMENT {
string id PK
string committeeRef FK
string documentRef FK
string role "report, opinion, position"
}
DOCUMENT ||--|{ PLENARY_DOCUMENT : "appears in"
DOCUMENT ||--|{ COMMITTEE_DOCUMENT : "assigned to"
erDiagram
VOTE_RECORD {
string id PK
string procedureRef
string sessionRef FK
DateString date
string voteType "rollcall, show of hands, electronic"
string subject
int votesFor
int votesAgainst
int abstentions
string result "adopted, rejected"
}
MEP_VOTE {
string id PK
string voteRecordRef FK
MEP_ID mepRef FK
string position "for, against, abstention, absent"
string politicalGroup
}
VOTE_RECORD ||--|{ MEP_VOTE : "composed of"
Branded types enforce semantic correctness for EP domain identifiers at both compile-time and runtime.
import { z } from 'zod';
// Procedure ID: YYYY/NNNN(TYPE) format
// Example: 2024/0001(COD), 2023/0089(INI)
const ProcedureIDSchema = z
.string()
.regex(/^\d{4}\/\d{4}\([A-Z]{2,4}\)$/)
.brand<'ProcedureID'>();
type ProcedureID = z.infer<typeof ProcedureIDSchema>;
// ISO 3166-1 alpha-2 country code
// Example: DE, FR, SE, PL
const CountryCodeSchema = z
.string()
.length(2)
.regex(/^[A-Z]{2}$/)
.brand<'CountryCode'>();
type CountryCode = z.infer<typeof CountryCodeSchema>;
// ISO 8601 date string
// Example: 2024-03-15
const DateStringSchema = z
.string()
.regex(/^\d{4}-\d{2}-\d{2}$/)
.brand<'DateString'>();
type DateString = z.infer<typeof DateStringSchema>;
// MEP identifier (positive integer)
const MEP_IDSchema = z
.number()
.int()
.positive()
.brand<'MEP_ID'>();
type MEP_ID = z.infer<typeof MEP_IDSchema>;
// Political group abbreviation
// Example: EPP, SD, Renew, Greens, ECR, ID, GUE
const PoliticalGroupSchema = z
.string()
.min(1)
.max(20)
.brand<'PoliticalGroup'>();
type PoliticalGroup = z.infer<typeof PoliticalGroupSchema>;
| Type | Format | Example | Validation Rule |
|---|---|---|---|
ProcedureID |
YYYY/NNNN(TYPE) |
2024/0001(COD) |
Regex: ^\d{4}\/\d{4}\([A-Z]{2,4}\)$ |
CountryCode |
AA |
DE, FR |
2 uppercase letters |
DateString |
YYYY-MM-DD |
2024-03-15 |
ISO 8601 format |
MEP_ID |
Integer | 12345 |
Positive integer |
PoliticalGroup |
String | EPP, SD |
1-20 chars |
Each of the 61 MCP tools has a corresponding Zod input schema. Representative examples:
// get_meps tool schema
const GetMEPsInputSchema = z.object({
country: CountryCodeSchema.optional(),
politicalGroup: PoliticalGroupSchema.optional(),
term: z.number().int().positive().optional(),
limit: z.number().int().min(1).max(100).default(50),
offset: z.number().int().min(0).default(0),
});
// get_mep_details tool schema
const GetMEPDetailsInputSchema = z.object({
mepId: MEP_IDSchema,
includeDeclarations: z.boolean().default(false),
includeAttendance: z.boolean().default(false),
});
// get_procedures tool schema
const GetProceduresInputSchema = z.object({
procedureId: ProcedureIDSchema.optional(),
type: z.enum(['COD', 'CNS', 'INI', 'RSP', 'BUD']).optional(),
status: z.enum(['ongoing', 'completed', 'withdrawn']).optional(),
fromDate: DateStringSchema.optional(),
toDate: DateStringSchema.optional(),
limit: z.number().int().min(1).max(100).default(20),
});
The EP API returns JSON-LD format. The server normalizes this to plain TypeScript objects:
{
"@context": "https://data.europarl.europa.eu/api/v2/",
"@graph": [
{
"@type": "ep:Member",
"@id": "https://data.europarl.europa.eu/api/v2/meps/12345",
"identifier": "12345",
"label": "Maria Example",
"country": "DE",
"politicalGroup": "EPP",
"mandateStart": "2024-07-16",
"mandateEnd": null
}
],
"meta": {
"total": 720,
"offset": 0,
"limit": 50
}
}
EP API JSON-LD β JSON.parse() β Extract @graph array β Map to typed objects β Zod validation β Branded types applied
The LRU cache uses deterministic key generation for all EP API calls:
| Pattern | Example Key | TTL |
|---|---|---|
mep:{id} |
mep:12345 |
15 min |
meps:list:{country}:{group}:{offset}:{limit} |
meps:list:DE:EPP:0:50 |
15 min |
procedure:{id} |
procedure:2024/0001(COD) |
15 min |
procedures:list:{type}:{status}:{from}:{to}:{limit} |
procedures:list:COD:ongoing:::20 |
15 min |
plenary:{id} |
plenary:20240315 |
15 min |
plenary:list:{from}:{to}:{limit} |
plenary:list:2024-01-01:2024-03-31:20 |
15 min |
votes:{id} |
votes:V-2024-001 |
15 min |
committee:{id} |
committee:ENVI |
15 min |
vocab:{type} |
vocab:countries |
60 min |
Cache Key Construction:
function buildCacheKey(client: string, params: Record<string, unknown>): string {
const sortedParams = Object.keys(params)
.sort()
.map(k => `${k}:${params[k] ?? ''}`)
.join(':');
return `${client}:${sortedParams}`;
}
See FUTURE_DATA_MODEL.md for planned enhancements including graph database support and temporal data models.