diff --git a/.github/workflows/comment-trigger.yml b/.github/workflows/comment-trigger.yml new file mode 100644 index 0000000..2865dab --- /dev/null +++ b/.github/workflows/comment-trigger.yml @@ -0,0 +1,90 @@ +name: Comment Trigger + +on: + issue_comment: + types: [created] + +permissions: + contents: read + pull-requests: write + actions: write + +jobs: + trigger-checks: + name: Trigger Checks from Comment + if: | + github.event.issue.pull_request && + (github.event.comment.body == '/test all' || + github.event.comment.body == '/test-all' || + github.event.comment.body == '/run-all-checks') && + (github.event.comment.author_association == 'OWNER' || + github.event.comment.author_association == 'MEMBER' || + github.event.comment.author_association == 'COLLABORATOR') + runs-on: ubuntu-latest + steps: + - name: Get PR branch + uses: actions/github-script@v7 + id: get-pr + with: + script: | + const pr = await github.rest.pulls.get({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.issue.number + }); + return pr.data.head.ref; + + - name: Add reaction to comment + uses: actions/github-script@v7 + with: + script: | + await github.rest.reactions.createForIssueComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: context.payload.comment.id, + content: 'rocket' + }); + + - name: Trigger Python CI + uses: actions/github-script@v7 + with: + script: | + await github.rest.actions.createWorkflowDispatch({ + owner: context.repo.owner, + repo: context.repo.repo, + workflow_id: 'python.yml', + ref: '${{ fromJSON(steps.get-pr.outputs.result) }}' + }); + + - name: Trigger Go CI + uses: actions/github-script@v7 + with: + script: | + await github.rest.actions.createWorkflowDispatch({ + owner: context.repo.owner, + repo: context.repo.repo, + workflow_id: 'go.yml', + ref: '${{ fromJSON(steps.get-pr.outputs.result) }}' + }); + + - name: Trigger TypeScript CI + uses: actions/github-script@v7 + with: + script: | + await github.rest.actions.createWorkflowDispatch({ + owner: context.repo.owner, + repo: context.repo.repo, + workflow_id: 'typescript.yml', + ref: '${{ fromJSON(steps.get-pr.outputs.result) }}' + }); + + - name: Comment on PR + uses: actions/github-script@v7 + with: + script: | + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + body: '🚀 All CI checks triggered! Check the Actions tab for progress.' + }); \ No newline at end of file diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml new file mode 100644 index 0000000..87636b9 --- /dev/null +++ b/.github/workflows/go.yml @@ -0,0 +1,80 @@ +name: Go CI +on: + push: + branches: [main] + paths: + - 'go/**' + - '.github/workflows/go.yml' + pull_request: + paths: + - 'go/**' + - '.github/workflows/go.yml' + workflow_dispatch: + +permissions: + contents: read + +jobs: + go-lint: + name: "Linting" + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - name: Set up Go + uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0 + with: + go-version: "^1.25" + - name: Check formatting + run: | + cd go/sdk + unformatted=$(gofmt -l .) + if [ -n "$unformatted" ]; then + echo "The following files are not properly formatted:" + echo "$unformatted" + exit 1 + fi + echo "All Go files are properly formatted" + - name: Run Go vet + run: | + cd go/sdk + go vet ./... + - name: Run staticcheck + uses: dominikh/staticcheck-action@288b4e28bae83c59f35f73651aeb5cab746a06fc # v1.4.0 + with: + install-go: false + version: "v0.6.1" + working-directory: go/sdk + + go-test: + name: "Unit Tests" + runs-on: ubuntu-latest + strategy: + matrix: + go: ["1.23", "1.24", "1.25"] + steps: + - name: Check out code + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - name: Set up Go + uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0 + with: + go-version: ${{ matrix.go }} + - name: Test + run: | + cd go/sdk + go test -v ./... + + go-race-test: + name: "Race Detection" + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - name: Set up Go + uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0 + with: + go-version: "1.24" + - name: Test with -race + run: | + cd go/sdk + go test -v -race ./... \ No newline at end of file diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml new file mode 100644 index 0000000..a2d43da --- /dev/null +++ b/.github/workflows/python.yml @@ -0,0 +1,67 @@ +name: Python CI +on: + push: + branches: [main] + paths: + - 'python/**' + - '.github/workflows/python.yml' + pull_request: + paths: + - 'python/**' + - '.github/workflows/python.yml' + workflow_dispatch: + +permissions: + contents: read + +jobs: + python-lint: + name: "Linting" + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + - name: Install dependencies + run: | + cd python/sdk + pip install -e . + pip install ruff mypy + - name: Run Ruff formatter check + run: | + cd python/sdk + ruff format --check . + - name: Run Ruff linter + run: | + cd python/sdk + ruff check . + - name: Run mypy + run: | + cd python/sdk + mypy src/mcp_ext_interceptors --ignore-missing-imports + + python-test: + name: "Unit Tests" + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.10", "3.11", "3.12"] + steps: + - name: Check out code + uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + cd python/sdk + pip install -e . + pip install pytest pytest-cov + - name: Run tests + run: | + cd python/sdk + pytest -v --cov=src/mcp_ext_interceptors \ No newline at end of file diff --git a/.github/workflows/status-check.yml b/.github/workflows/status-check.yml new file mode 100644 index 0000000..63d22f4 --- /dev/null +++ b/.github/workflows/status-check.yml @@ -0,0 +1,138 @@ +name: Status Check + +# This workflow enforces that all relevant CI checks pass based on which files changed. +# It's the only required status check in branch protection settings. + +on: + pull_request: + types: [opened, synchronize, reopened] + workflow_run: + workflows: ["Python CI", "Go CI", "TypeScript CI"] + types: [completed] + +permissions: + contents: read + statuses: write + checks: write + pull-requests: read + +jobs: + verify-required-checks: + name: Verify Required Checks + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' || github.event.workflow_run.conclusion != 'cancelled' + steps: + - name: Check out code + uses: actions/checkout@v4 + + - name: Get PR number + id: pr + run: | + if [ "${{ github.event_name }}" == "workflow_run" ]; then + PR_NUMBER=$(gh pr list --head "${{ github.event.workflow_run.head_branch }}" --json number --jq '.[0].number') + echo "number=$PR_NUMBER" >> $GITHUB_OUTPUT + else + echo "number=${{ github.event.pull_request.number }}" >> $GITHUB_OUTPUT + fi + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Get changed files + id: changed-files + uses: dorny/paths-filter@v3 + with: + filters: | + python: + - 'python/**' + - '.github/workflows/python.yml' + go: + - 'go/**' + - '.github/workflows/go.yml' + typescript: + - 'typescript/**' + - '.github/workflows/typescript.yml' + + - name: Check required statuses + uses: actions/github-script@v7 + with: + script: | + const owner = context.repo.owner; + const repo = context.repo.repo; + const ref = context.payload.pull_request?.head.sha || context.payload.workflow_run?.head_sha; + + // Define required checks per language + const requiredChecks = { + python: [ + 'Python CI / Linting', + 'Python CI / Unit Tests (3.10)', + 'Python CI / Unit Tests (3.11)', + 'Python CI / Unit Tests (3.12)' + ], + go: [ + 'Go CI / Linting', + 'Go CI / Unit Tests (1.23)', + 'Go CI / Unit Tests (1.24)', + 'Go CI / Unit Tests (1.25)', + 'Go CI / Race Detection' + ], + typescript: [ + 'TypeScript CI / Linting', + 'TypeScript CI / Unit Tests (20)', + 'TypeScript CI / Unit Tests (22)' + ] + }; + + // Get changed files from previous step + const changedFiles = { + python: ${{ steps.changed-files.outputs.python }} === 'true', + go: ${{ steps.changed-files.outputs.go }} === 'true', + typescript: ${{ steps.changed-files.outputs.typescript }} === 'true' + }; + + // Collect all required checks based on changes + let allRequiredChecks = []; + for (const [lang, changed] of Object.entries(changedFiles)) { + if (changed) { + allRequiredChecks = allRequiredChecks.concat(requiredChecks[lang]); + } + } + + if (allRequiredChecks.length === 0) { + console.log('No language files changed, no checks required'); + return; + } + + // Get all check runs for this commit + const { data: checkRuns } = await github.rest.checks.listForRef({ + owner, + repo, + ref + }); + + // Verify all required checks have passed + const checkStatuses = {}; + for (const check of checkRuns.check_runs) { + checkStatuses[check.name] = check.conclusion; + } + + let allPassed = true; + let failureMessage = ''; + + for (const requiredCheck of allRequiredChecks) { + const status = checkStatuses[requiredCheck]; + if (!status) { + allPassed = false; + failureMessage += `❌ ${requiredCheck}: Not started\\n`; + } else if (status !== 'success') { + allPassed = false; + failureMessage += `❌ ${requiredCheck}: ${status}\\n`; + } else { + failureMessage += `✅ ${requiredCheck}: ${status}\\n`; + } + } + + if (!allPassed) { + core.setFailed(`Required checks have not passed:\\n${failureMessage}`); + } else { + console.log(`All required checks passed:\\n${failureMessage}`); + } \ No newline at end of file diff --git a/.github/workflows/typescript.yml b/.github/workflows/typescript.yml new file mode 100644 index 0000000..fba0aa6 --- /dev/null +++ b/.github/workflows/typescript.yml @@ -0,0 +1,61 @@ +name: TypeScript CI +on: + push: + branches: [main] + paths: + - 'typescript/**' + - '.github/workflows/typescript.yml' + pull_request: + paths: + - 'typescript/**' + - '.github/workflows/typescript.yml' + workflow_dispatch: + +permissions: + contents: read + +jobs: + typescript-lint: + name: "Linting" + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@v6 + - name: Set up Node.js + uses: actions/setup-node@v6 + with: + node-version: "20" + - name: Install dependencies + run: | + cd typescript/sdk + npm install + - name: Run TypeScript compiler check + run: | + cd typescript/sdk + npm run build + - name: Run ESLint + run: | + cd typescript/sdk + npm run lint + + typescript-test: + name: "Unit Tests" + runs-on: ubuntu-latest + strategy: + matrix: + node-version: ["20", "22"] + steps: + - name: Check out code + uses: actions/checkout@v6 + - name: Set up Node.js + uses: actions/setup-node@v6 + with: + node-version: ${{ matrix.node-version }} + - name: Install dependencies + run: | + cd typescript/sdk + npm install + - name: Run tests + run: | + cd typescript/sdk + npm test \ No newline at end of file diff --git a/.gitignore b/.gitignore index aaadf73..24f0e4d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,3 @@ -# If you prefer the allow list template instead of the deny list, see community template: -# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore -# # Binaries for programs and plugins *.exe *.exe~ @@ -30,3 +27,44 @@ go.work.sum # Editor/IDE # .idea/ # .vscode/ + +# Node.js / TypeScript +node_modules/ +dist/ +*.tsbuildinfo +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Python +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg +.venv/ +venv/ +ENV/ +.pytest_cache/ +.mypy_cache/ +.ruff_cache/ +htmlcov/ +.coverage +.coverage.* +coverage.xml +*.cover diff --git a/README.md b/README.md new file mode 100644 index 0000000..248dd99 --- /dev/null +++ b/README.md @@ -0,0 +1,75 @@ +# MCP Interceptors + +Multi-language implementations of the Model Context Protocol (MCP) interceptor framework. + +For the full specification and design details, see: [SEP-1763: Interceptor Framework for Model Context Protocol](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1763) + +## Implementations + +| Language | Directory | Package | Status | +|----------|-----------|---------|--------| +| Go | `go/sdk/` | `github.com/modelcontextprotocol/ext-interceptors/go/sdk` | Planned | +| Python | `python/sdk/` | `mcp-ext-interceptors` | Planned | +| TypeScript | `typescript/sdk/` | `@ext-modelcontextprotocol/interceptors` | Planned | + + +## CI/CD + +This monorepo uses **path-based CI workflows** to efficiently test only what changes: + +### How It Works + +1. **Language-specific workflows** (`python.yml`, `go.yml`, `typescript.yml`) + - Only trigger when their language directory or workflow file changes + - Run all tests, linting, and checks for that language + +2. **Status check workflow** (`status-check.yml`) + - Runs on every PR to verify required checks passed + - Determines what needs to pass based on which files changed + - This is the only required check in branch protection + +### Examples + +- Change `python/sdk/file.py` → Only Python CI runs → PR requires Python checks to pass +- Change both Go and TypeScript files → Both CIs run → PR requires both to pass +- Change only `README.md` → No language CIs run → PR can merge immediately + +### Forcing All Checks + +To run all language checks regardless of changed files: +- **In a PR**: Comment `/test all` (only works for repo owners/members/collaborators) +- **Manually**: Use GitHub Actions UI or CLI to trigger individual workflows + +### Adding New Required Checks + +1. **Add your check** to the appropriate language workflow (e.g., `python.yml`): + ```yaml + python-security-scan: + name: "Security Scan" + runs-on: ubuntu-latest + steps: + - name: Run security checks + run: # your commands here + ``` + +2. **Update the status check** in `.github/workflows/status-check.yml`: + ```javascript + const requiredChecks = { + python: [ + 'Python CI / Linting', + 'Python CI / Unit Tests (3.10)', + // ... existing checks ... + 'Python CI / Security Scan' // ← Add your new check + ], + ``` + +3. **Submit PR** - Your new check is now required for all relevant changes! + +## License + +Apache License 2.0 - See LICENSE file for details + +## Resources + +- [Interceptor Framework Specification (SEP-1763)](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1763) - Full specification and design details +- [Model Context Protocol](https://modelcontextprotocol.io/specification) diff --git a/conformance/.gitkeep b/conformance/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/go/sdk/README.md b/go/sdk/README.md new file mode 100644 index 0000000..a24c5a9 --- /dev/null +++ b/go/sdk/README.md @@ -0,0 +1,3 @@ +# MCP Interceptors - Go Implementation + +This will contain the Go implementation of the MCP Interceptors based on [SEP-1763](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1763). diff --git a/go/sdk/examples/.gitkeep b/go/sdk/examples/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/go/sdk/go.mod b/go/sdk/go.mod new file mode 100644 index 0000000..0584e8f --- /dev/null +++ b/go/sdk/go.mod @@ -0,0 +1,4 @@ +module github.com/modelcontextprotocol/ext-interceptors/go/sdk + +go 1.23.0 + diff --git a/go/sdk/interceptors/interceptor.go b/go/sdk/interceptors/interceptor.go new file mode 100644 index 0000000..3cac4e1 --- /dev/null +++ b/go/sdk/interceptors/interceptor.go @@ -0,0 +1,6 @@ +// Copyright 2025 The MCP Interceptors Authors. All rights reserved. +// Use of this source code is governed by a Apache-2.0 +// license that can be found in the LICENSE file. + +// Package interceptors implements MCP Interceptors based on [SEP-1763](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1763). +package interceptors diff --git a/go/sdk/interceptors/interceptor_test.go b/go/sdk/interceptors/interceptor_test.go new file mode 100644 index 0000000..fb110b9 --- /dev/null +++ b/go/sdk/interceptors/interceptor_test.go @@ -0,0 +1,14 @@ +// Copyright 2025 The MCP Interceptors Authors. All rights reserved. +// Use of this source code is governed by a Apache-2.0 +// license that can be found in the LICENSE file. + +package interceptors + +import "testing" + +func TestPlaceholder(t *testing.T) { + // Placeholder test to ensure go test runs successfully + if false { + t.Error("This should not happen") + } +} diff --git a/go/sdk/internal/.gitkeep b/go/sdk/internal/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/python/sdk/README.md b/python/sdk/README.md new file mode 100644 index 0000000..7ac3e6f --- /dev/null +++ b/python/sdk/README.md @@ -0,0 +1,31 @@ +# MCP Interceptors Python SDK + +Python implementation of the Model Context Protocol (MCP) interceptor framework. + +## Installation + +```bash +pip install mcp-ext-interceptors +``` + +## Usage + +```python +from mcp_ext_interceptors import Interceptor + +# Example usage will be added as the implementation progresses +``` + +## Development + +```bash +# Install in development mode +pip install -e . + +# Run tests (when available) +pytest +``` + +## License + +Apache License 2.0 - See LICENSE file in the root directory for details. \ No newline at end of file diff --git a/python/sdk/pyproject.toml b/python/sdk/pyproject.toml new file mode 100644 index 0000000..41f887a --- /dev/null +++ b/python/sdk/pyproject.toml @@ -0,0 +1,71 @@ +[project] +name = "mcp-ext-interceptors" +version = "0.1.0" +description = "Python SDK for Model Context Protocol interceptors" +authors = [{name = "The MCP Interceptors Authors"}] +readme = "README.md" +license = {text = "Apache-2.0"} +requires-python = ">=3.10" +classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "License :: OSI Approved :: Apache Software License", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", +] +dependencies = [ + "mcp>=1.0.0", +] + +[project.urls] +"Homepage" = "https://github.com/modelcontextprotocol/ext-interceptors" +"Bug Reports" = "https://github.com/modelcontextprotocol/ext-interceptors/issues" +"Source" = "https://github.com/modelcontextprotocol/ext-interceptors" + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.hatch.build.targets.wheel] +packages = ["src/mcp_ext_interceptors"] + +[tool.ruff] +line-length = 120 +target-version = "py310" + +[tool.ruff.lint] +select = [ + "E", # pycodestyle + "F", # pyflakes + "I", # isort + "UP", # pyupgrade + "C4", # flake8-comprehensions + "PERF", # Perflint +] + +[tool.ruff.lint.per-file-ignores] +"__init__.py" = ["F401"] # Allow unused imports in __init__.py + +[tool.ruff.format] +quote-style = "double" +indent-style = "space" + +[tool.mypy] +python_version = "3.10" +warn_return_any = true +warn_unused_configs = true +disallow_untyped_defs = true +ignore_missing_imports = true + +[tool.pyright] +typeCheckingMode = "basic" +include = ["src/mcp_ext_interceptors", "tests"] +venvPath = "." +venv = ".venv" + +[tool.pytest.ini_options] +testpaths = ["tests"] +python_files = ["test_*.py", "*_test.py"] +addopts = "--strict-markers --verbose" \ No newline at end of file diff --git a/python/sdk/src/mcp_ext_interceptors/__init__.py b/python/sdk/src/mcp_ext_interceptors/__init__.py new file mode 100644 index 0000000..9481551 --- /dev/null +++ b/python/sdk/src/mcp_ext_interceptors/__init__.py @@ -0,0 +1,10 @@ +# Copyright 2025 The MCP Interceptors Authors. All rights reserved. +# Use of this source code is governed by a Apache-2.0 +# license that can be found in the LICENSE file. + +""" +MCP Interceptors Python SDK + +Implements MCP Interceptors based on SEP-1763: +https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1763 +""" diff --git a/python/sdk/src/mcp_ext_interceptors/interceptor.py b/python/sdk/src/mcp_ext_interceptors/interceptor.py new file mode 100644 index 0000000..376a2ef --- /dev/null +++ b/python/sdk/src/mcp_ext_interceptors/interceptor.py @@ -0,0 +1,8 @@ +# Copyright 2025 The MCP Interceptors Authors. All rights reserved. +# Use of this source code is governed by a Apache-2.0 +# license that can be found in the LICENSE file. + +""" +Package interceptors implements MCP Interceptors based on SEP-1763: +https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1763 +""" diff --git a/python/sdk/src/mcp_ext_interceptors/py.typed b/python/sdk/src/mcp_ext_interceptors/py.typed new file mode 100644 index 0000000..af47fd3 --- /dev/null +++ b/python/sdk/src/mcp_ext_interceptors/py.typed @@ -0,0 +1,2 @@ +# Marker file for PEP 561 +# This file indicates that this package supports type checking \ No newline at end of file diff --git a/python/sdk/tests/__init__.py b/python/sdk/tests/__init__.py new file mode 100644 index 0000000..5a155ac --- /dev/null +++ b/python/sdk/tests/__init__.py @@ -0,0 +1 @@ +# Tests for mcp-ext-interceptors diff --git a/python/sdk/tests/test_interceptors.py b/python/sdk/tests/test_interceptors.py new file mode 100644 index 0000000..1b68306 --- /dev/null +++ b/python/sdk/tests/test_interceptors.py @@ -0,0 +1,10 @@ +# Copyright 2025 The MCP Interceptors Authors. All rights reserved. +# Use of this source code is governed by a Apache-2.0 +# license that can be found in the LICENSE file. + +"""Tests for MCP interceptors.""" + + +def test_placeholder(): + """Placeholder test to ensure pytest runs successfully.""" + assert True diff --git a/typescript/sdk/.eslintrc.json b/typescript/sdk/.eslintrc.json new file mode 100644 index 0000000..db42afc --- /dev/null +++ b/typescript/sdk/.eslintrc.json @@ -0,0 +1,24 @@ +{ + "root": true, + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": "latest", + "sourceType": "module", + "project": "./tsconfig.json" + }, + "plugins": ["@typescript-eslint"], + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:@typescript-eslint/recommended-requiring-type-checking" + ], + "env": { + "node": true, + "es2022": true + }, + "rules": { + "@typescript-eslint/explicit-module-boundary-types": "error", + "@typescript-eslint/no-explicit-any": "error", + "@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }] + } +} \ No newline at end of file diff --git a/typescript/sdk/.nvmrc b/typescript/sdk/.nvmrc new file mode 100644 index 0000000..2edeafb --- /dev/null +++ b/typescript/sdk/.nvmrc @@ -0,0 +1 @@ +20 \ No newline at end of file diff --git a/typescript/sdk/README.md b/typescript/sdk/README.md new file mode 100644 index 0000000..37523a0 --- /dev/null +++ b/typescript/sdk/README.md @@ -0,0 +1,40 @@ +# MCP Interceptors TypeScript SDK + +TypeScript implementation of the Model Context Protocol (MCP) interceptor framework. + +## Installation + +```bash +npm install @ext-modelcontextprotocol/interceptors +``` + +## Usage + +```typescript +import { Interceptor } from '@ext-modelcontextprotocol/interceptors'; + +// The MCP SDK is available as a peer dependency +import { Client, Server } from '@modelcontextprotocol/sdk'; + +// Example usage will be added as the implementation progresses +``` + +## Development + +```bash +# Install dependencies +npm install + +# Build +npm run build + +# Run tests +npm test + +# Lint +npm run lint +``` + +## License + +Apache License 2.0 - See LICENSE file in the root directory for details. diff --git a/typescript/sdk/package.json b/typescript/sdk/package.json new file mode 100644 index 0000000..ce494aa --- /dev/null +++ b/typescript/sdk/package.json @@ -0,0 +1,56 @@ +{ + "name": "@ext-modelcontextprotocol/interceptors", + "version": "0.1.0", + "description": "TypeScript SDK for Model Context Protocol interceptors", + "type": "module", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + } + }, + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "engines": { + "node": ">=20" + }, + "scripts": { + "build": "tsc", + "test": "vitest run", + "test:watch": "vitest", + "lint": "eslint src --ext .ts", + "prepublishOnly": "npm run build" + }, + "keywords": [ + "mcp", + "model-context-protocol", + "interceptors" + ], + "author": "The MCP Interceptors Authors", + "license": "Apache-2.0", + "repository": { + "type": "git", + "url": "https://github.com/modelcontextprotocol/ext-interceptors.git", + "directory": "typescript/sdk" + }, + "bugs": { + "url": "https://github.com/modelcontextprotocol/ext-interceptors/issues" + }, + "homepage": "https://github.com/modelcontextprotocol/ext-interceptors", + "files": [ + "dist", + "src", + "README.md" + ], + "peerDependencies": { + "@modelcontextprotocol/sdk": "^1.0.0" + }, + "devDependencies": { + "@types/node": "^20.0.0", + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser": "^6.0.0", + "eslint": "^8.0.0", + "typescript": "^5.0.0", + "vitest": "^2.0.0" + } +} \ No newline at end of file diff --git a/typescript/sdk/src/index.ts b/typescript/sdk/src/index.ts new file mode 100644 index 0000000..07d70db --- /dev/null +++ b/typescript/sdk/src/index.ts @@ -0,0 +1,12 @@ +// Copyright 2025 The MCP Interceptors Authors. All rights reserved. +// Use of this source code is governed by a Apache-2.0 +// license that can be found in the LICENSE file. + +/** + * MCP Interceptors TypeScript SDK + * + * Implements MCP Interceptors based on SEP-1763: + * https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1763 + */ + +export * from './interceptors.js'; \ No newline at end of file diff --git a/typescript/sdk/src/interceptors.test.ts b/typescript/sdk/src/interceptors.test.ts new file mode 100644 index 0000000..67f270a --- /dev/null +++ b/typescript/sdk/src/interceptors.test.ts @@ -0,0 +1,11 @@ +// Copyright 2025 The MCP Interceptors Authors. All rights reserved. +// Use of this source code is governed by a Apache-2.0 +// license that can be found in the LICENSE file. + +import { describe, it, expect } from 'vitest'; + +describe('Interceptors', () => { + it('should be defined', () => { + expect(true).toBe(true); + }); +}); \ No newline at end of file diff --git a/typescript/sdk/src/interceptors.ts b/typescript/sdk/src/interceptors.ts new file mode 100644 index 0000000..5568db2 --- /dev/null +++ b/typescript/sdk/src/interceptors.ts @@ -0,0 +1,12 @@ +// Copyright 2025 The MCP Interceptors Authors. All rights reserved. +// Use of this source code is governed by a Apache-2.0 +// license that can be found in the LICENSE file. + +/** + * Core interceptor functionality for MCP + * + * Based on SEP-1763: + * https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1763 + */ + +// Placeholder for interceptor implementation \ No newline at end of file diff --git a/typescript/sdk/tsconfig.json b/typescript/sdk/tsconfig.json new file mode 100644 index 0000000..95b0198 --- /dev/null +++ b/typescript/sdk/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "target": "esnext", + "module": "NodeNext", + "lib": ["esnext"], + "outDir": "./dist", + "rootDir": "./src", + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "strict": true, + "isolatedModules": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "moduleResolution": "NodeNext", + "resolveJsonModule": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist"] +} \ No newline at end of file diff --git a/typescript/sdk/vitest.config.js b/typescript/sdk/vitest.config.js new file mode 100644 index 0000000..bfa5448 --- /dev/null +++ b/typescript/sdk/vitest.config.js @@ -0,0 +1,10 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + environment: 'node', + include: ['src/**/*.test.ts', 'test/**/*.test.ts'], + exclude: ['**/node_modules/**', '**/dist/**'] + } +}); \ No newline at end of file