Hi there! We're thrilled that you'd like to contribute to this project. Your help is essential for keeping it great.
Please note that this project is released with a Contributor Code of Conduct. By participating in this project you agree to abide by its terms.
If you have suggestions for how this project could be improved, or want to report a bug, open an issue! We'd love all and any contributions. If you have questions, too, we'd love to hear them.
We'd also love PRs. If you're thinking of a large PR, we advise opening up an issue first to talk about it, though! Look at the links below if you're not sure how to open a PR.
| Requirement | Minimum Version | Check |
|---|---|---|
| Node.js | 26.0.0 | node --version |
| npm | 10.0.0 | npm --version |
| Git | 2.x | git --version |
# 1. Fork the repository, then clone your fork
git clone https://github.com/<your-username>/European-Parliament-MCP-Server.git
cd European-Parliament-MCP-Server
# 2. Install dependencies (uses package-lock.json for reproducibility)
npm install
# 3. Verify build works
npm run build
# 4. Run all unit tests to confirm your environment is healthy
npm test
# 5. Optional: run type-checking separately
npm run type-check
# Start the server in watch mode (auto-recompile on change)
npm run dev
# Run unit tests in watch mode
npm run test:watch
# Run unit tests with coverage report
npm run test:coverage
# Lint source files
npm run lint
# Auto-fix linting issues
npm run lint:fix
# Check code formatting
npm run format
# Check for dead code / unused exports
npm run knip
# Security audit
npm audit
# License compliance check
npm run test:licenses
Create a .env.test file for integration testing:
# Set to 'true' to run tests against the real EP API (rate-limited)
EP_INTEGRATION_TESTS=false
# Optional: override API base URL for testing against a mirror
EP_API_URL=https://data.europarl.europa.eu/api/v2
โ ๏ธ Never commit
.envfiles โ they are listed in.gitignore.
All branches must follow the pattern: <type>/<short-description>
| Type | Purpose | Example |
|---|---|---|
feature/ |
New functionality | feature/add-budget-tool |
fix/ |
Bug fix | fix/rate-limiter-race-condition |
docs/ |
Documentation only | docs/improve-jsdoc-getMEPs |
chore/ |
Maintenance, deps, CI | chore/upgrade-vitest-4 |
test/ |
Test improvements | test/add-e2e-coalition-tool |
perf/ |
Performance improvements | perf/cache-key-normalization |
security/ |
Security fixes | security/upgrade-undici |
npm installnpm testgit checkout -b feature/my-branch-nameHere are a few things you can do that will increase the likelihood of your pull request being accepted:
npm run lint and npm run format before committingWhen you submit a pull request, our automated labeler will automatically apply labels based on the files you've changed:
mcp-tools - Changes to MCP tool implementations (src/tools/)mcp-resources - Changes to MCP resources (src/resources/)mcp-prompts - Changes to MCP prompts (src/prompts/)mcp-protocol - MCP protocol changesep-api - European Parliament API integrationep-data - European Parliament data handlingmeps - MEP-related changesplenary - Plenary session featurescommittees - Committee featuresdocuments - Document handlingfeature - New features or functionalityenhancement - Improvements to existing featuresbug - Bug fixesdocumentation - Documentation updatesdependencies - Dependency updatessecurity - Security improvementsperformance - Performance optimizationstesting - Test improvementsgdpr - GDPR compliance changesisms-compliance - ISMS policy compliancesecurity - Security enhancementsYou can also manually add labels by including them in your PR description using checkboxes:
- [x] ๐ New Feature/Enhancement
- [x] ๐ MCP Tools
- [x] ๐๏ธ European Parliament API
We follow Conventional Commits specification:
<type>(<scope>): <subject>
<body>
<footer>
Types:
feat: New featurefix: Bug fixdocs: Documentation changesstyle: Code style changes (formatting, missing semicolons, etc)refactor: Code refactoringperf: Performance improvementstest: Adding or updating testsbuild: Changes to build system or dependenciesci: CI/CD changeschore: Other changes that don't modify src or test filesExamples:
feat(mcp-tools): add getMEPs tool for fetching MEP information
fix(ep-api): handle rate limiting in European Parliament API calls
docs: update README with new MCP tools documentation
test: add unit tests for plenary session tools
build(deps): update @modelcontextprotocol/sdk to v1.0.5
Work in Progress pull requests are also welcome to get feedback early on, or if there is something blocked you.
See docs/TOOL_DEVELOPMENT.md for the full step-by-step guide. Here is the quick summary:
# Create src/tools/myNewTool.ts
# Use an existing tool (e.g., getMEPs.ts) as a template
Add the input schema to src/schemas/europeanParliament.ts:
export const MyNewToolSchema = z.object({
subjectId: z.string().min(1).max(200).describe('Subject identifier'),
limit: z.number().int().min(1).max(100).default(50),
});
Follow the two-layer error pattern:
export async function handleMyNewTool(args: unknown): Promise<ToolResult> {
const params = MyNewToolSchema.parse(args); // Layer 1: Zod validation
try {
const result = await epClient.someMethod(params);
return buildToolResponse(result);
} catch (error) {
const msg = error instanceof Error ? error.message : 'Unknown error';
throw new Error(`Failed to fetch data: ${msg}`);
}
}
export const myNewToolMetadata = {
name: 'my_new_tool',
description: 'One paragraph explaining what this tool does and its parameters.',
inputSchema: { type: 'object' as const, properties: { ... } },
};
Then add the import and routing case to the server's tool registry.
Create src/tools/myNewTool.test.ts covering:
ToolResultZodError thrownOSINT tools only: any tool registered with category: 'osint' is automatically enrolled in the cross-tool contract suite (tests/integration/osint/contract.test.ts) and the per-tool golden-snapshot suite (tests/integration/osint/snapshots.test.ts). Add a minimal-valid input entry to OSINT_TOOL_INPUTS in tests/fixtures/osint/index.ts (single source of truth โ both suites import from it), and ensure your tool follows the no-silent-zero policy (see below).
Every OSINT tool returns the OsintStandardOutput envelope: confidenceLevel, methodology, dataFreshness, sourceAttribution, dataQualityWarnings. The policy: no numeric field outside dataQualityWarnings may be silently zero because a data source was unavailable. When a source is missing or empty, the tool SHOULD either:
confidenceLevel to LOW or MEDIUM and push a human-readable entry into dataQualityWarnings explaining the unavailability, ORconfidenceLevel = HIGH only when the remaining data is sufficient to fully back every non-warning numeric metric in the payload.The contract test enforces the observable invariant: if confidenceLevel is LOW/MEDIUM, dataQualityWarnings MUST be non-empty. A degraded confidence level with no warning fails the test. See INTEGRATION_TESTING.md ยง "OSINT QA Harness" for the full policy.
The golden-snapshot suite (tests/integration/osint/snapshots.test.ts) diff-asserts every OSINT tool's response against a checked-in snapshot for two fixture variants (empty-path, hot-path) โ 30 snapshots total. A diff means a methodology change has occurred (scoring weight moved, classification bucket changed, attribution list re-ordered).
If the diff is intentional:
# Regenerate the affected snapshots
npm run test:osint:snapshots -- -u
# Inspect every diff (snapshots live at tests/integration/osint/__snapshots__/)
git diff tests/integration/osint/__snapshots__/
# Acknowledge the refresh in your PR
# โ tick "OSINT snapshot refresh acknowledged" in PULL_REQUEST_TEMPLATE.md
Reviewer guidance: never approve a PR that touches tests/integration/osint/__snapshots__/ without the acknowledgement checkbox ticked AND a justification for the methodology change in the PR description. Snapshot diffs are the regression-detection line for OSINT scoring; silently rubber-stamping them defeats the purpose of the suite. See INTEGRATION_TESTING.md ยง "OSINT QA Harness โ Golden Snapshots" for the full workflow.
The contract suite catches structural envelope violations but cannot detect logic regressions in scoring/anomaly methodology. The dedicated osint-qa CI workflow runs Stryker against the 15 OSINT tool files plus their seven shared utilities (votingBaseline.ts, graphAlgorithms.ts, networkVotingSimilarity.ts, effectivenessAggregator.ts, lifecycleStatistics.ts, doceoMepAggregator.ts, politicalGroupNormalization.ts).
Run the mutation suite locally:
# Full mutation run (โ10โ15 min on a laptop, scoped per stryker.config.json)
npm run test:mutation
# CI-equivalent invocation
npm run test:mutation:ci
Reports are written to builds/stryker/mutation-report.html and .json.
Acceptable surviving mutants:
console.error / auditSink log messages have no observable effect and may be left surviving.?? 0 / || [] fallbacks unreachable from existing fixtures โ annotate with // Stryker disable next-line and a one-line justification rather than adding contrived tests.Everything else โ boundary mutations on percentiles, anomaly thresholds, voting-similarity scores, lifecycle bucketing, coalition strength tiers, sentiment classification cuts โ must be killed by a test. If a survivor lands on a DOCEO-touching tool (assessMepInfluence, detectVotingAnomalies, sentimentTracker, networkAnalysis, analyzeCoalitionDynamics), add a regression test before merging.
The mutation job is currently continue-on-error: true while we observe the baseline score on main. Once stable, thresholds.break in stryker.config.json will be promoted from null to 70, making the job a required check.
The server uses a lightweight Dependency Injection (DI) container defined in src/di/.
src/di/tokens.ts)import { TOKENS } from './di/tokens.js';
// Available tokens:
TOKENS.EPClient // EuropeanParliamentClient singleton
TOKENS.RateLimiter // Token-bucket rate limiter
TOKENS.MetricsService // Performance metrics collector
TOKENS.AuditLogger // GDPR-compliant audit logger
TOKENS.HealthService // Server health-check service
import { createDefaultContainer } from './di/container.js';
import { TOKENS } from './di/tokens.js';
import type { MetricsService } from './services/MetricsService.js';
// In server startup:
const container = createDefaultContainer();
// Resolve a singleton:
const metrics = container.resolve<MetricsService>(TOKENS.MetricsService);
metrics.recordRequest('get_meps', 42);
createDefaultContainer() in src/di/container.tsepClient singleton imported directly from
src/clients/europeanParliamentClient.ts (it is wired into the container internally)All PRs must maintain these coverage thresholds:
| Metric | Minimum |
|---|---|
| Lines | 80% |
| Statements | 80% |
| Functions | 80% |
| Branches | 70% |
| Security-critical code | 95% |
# Unit tests (fast, no external dependencies)
npm run test:unit
# Unit tests with coverage report
npm run test:coverage
# Integration tests against real EP API (opt-in)
EP_INTEGRATION_TESTS=true npm run test:integration
# Integration tests with fixture capture
EP_INTEGRATION_TESTS=true EP_SAVE_FIXTURES=true npm run test:integration
# End-to-end tests via MCP stdio client
npm run test:e2e
# Performance benchmarks
npm run test:performance
# All test suites
npm run test:all
# Watch mode for TDD
npm run test:watch
src/tools/myTool.test.ts # Unit tests co-located with tool
tests/integration/ # Integration tests (EP API)
tests/e2e/ # End-to-end MCP client tests
tests/performance/ # Performance benchmarks
tests/fixtures/ # Shared mock data
tests/helpers/ # Test utilities (retry, measureTime, etc.)
Reviewers will check:
any, no unvalidated unknown beyond the handler entry point@param, @returns, @throws, @examplebuildToolResponse, schema naming, etc.)Before requesting a review on any PR touching src/:
.parse(args) as first handler line)min() and max() length constraintsconsole.log of sensitive data (MEP personal data, error internals)npm audit โ zero high/critical findingsSC-002 (Input Validation), AC-003 (Least Privilege)This project generates a Software Bill of Materials (SBOM) for every release and implements SLSA Level 3 build provenance to ensure supply chain transparency and security.
Build Attestations (SLSA Level 3):
gh attestation verify <artifact> --owner Hack23 --repo European-Parliament-MCP-Servernpm view european-parliament-mcp-server dist.attestationsSBOM Generation:
Accessing Security Artifacts:
sbom.spdx.json and sbom.cyclonedx.jsonprovenance.intoto.jsonlchecksums.txtFor more details, see: