Goal: Split mcp-server into two packages:
mcp-core: Private workspace package with shared code (tools, API client, server builder)mcp-stdio: Published package (keeping name@sentry/mcp-server) that fully bundles all core code
Status: Planning phase - not yet implemented
- Core:
@sentry/mcp-core(private, not published to npm) - Stdio:
@sentry/mcp-server(keep existing name for no breaking changes)
- Approach: DevDependency + Full Bundling
- mcp-stdio: Uses
@sentry/mcp-coreasdevDependency, bundles everything at build time - mcp-cloudflare: Uses
@sentry/mcp-coreas workspace dependency (private package, not published) - Publishing: mcp-core never published, only mcp-stdio published to npm
- Tests move with their code
- CLI/stdio tests → mcp-stdio package
- Core tests stay in mcp-core package
packages/
├── mcp-server/ # Contains everything (tools, API, CLI, stdio transport)
├── mcp-cloudflare/ # Imports from @sentry/mcp-server
├── mcp-server-mocks/
├── mcp-server-evals/
└── mcp-test-client/
packages/
├── mcp-core/ # Shared code (private package)
│ ├── src/
│ │ ├── api-client/ # Sentry API client
│ │ ├── tools/ # 19 MCP tools
│ │ ├── internal/ # Utilities, agents, helpers
│ │ ├── telem/ # Logging, Sentry integration
│ │ ├── utils/ # Shared utilities
│ │ ├── test-utils/ # Test helpers
│ │ ├── server.ts # buildServer() function
│ │ ├── types.ts # ServerContext, etc.
│ │ ├── permissions.ts
│ │ ├── skills.ts
│ │ ├── schema.ts
│ │ ├── errors.ts
│ │ ├── constants.ts
│ │ └── version.ts
│ └── scripts/ # Build scripts
│
├── mcp-stdio/ # CLI/stdio package (published)
│ ├── src/
│ │ ├── cli/ # MOVED from mcp-server
│ │ │ ├── parse.ts
│ │ │ ├── resolve.ts
│ │ │ ├── types.ts
│ │ │ └── usage.ts
│ │ ├── transports/ # MOVED from mcp-server
│ │ │ └── stdio.ts
│ │ └── index.ts # MOVED from mcp-server (CLI entry)
│ └── package.json # Bundles all of mcp-core
│
├── mcp-cloudflare/ # Uses workspace dep on mcp-core
├── mcp-server-mocks/
├── mcp-server-evals/
└── mcp-test-client/
From packages/mcp-server/src/:
src/cli/parse.ts → mcp-stdio/src/cli/parse.ts
src/cli/parse.test.ts → mcp-stdio/src/cli/parse.test.ts
src/cli/resolve.ts → mcp-stdio/src/cli/resolve.ts
src/cli/resolve.test.ts → mcp-stdio/src/cli/resolve.test.ts
src/cli/types.ts → mcp-stdio/src/cli/types.ts
src/cli/usage.ts → mcp-stdio/src/cli/usage.ts
src/transports/stdio.ts → mcp-stdio/src/transports/stdio.ts
src/index.ts → mcp-stdio/src/index.ts
Everything else in packages/mcp-server/ stays, package renamed to mcp-core:
src/api-client/ ✅ STAY - shared API client
src/tools/ ✅ STAY - all 19 tools
src/internal/ ✅ STAY - utilities, agents
src/telem/ ✅ STAY - logging, Sentry
src/utils/ ✅ STAY - shared utilities
src/test-utils/ ✅ STAY - test helpers
src/server.ts ✅ STAY - buildServer()
src/types.ts ✅ STAY - ServerContext
src/permissions.ts ✅ STAY - shared
src/skills.ts ✅ STAY - shared
src/schema.ts ✅ STAY - shared schemas
src/errors.ts ✅ STAY - error classes
src/constants.ts ✅ STAY - shared constants
src/version.ts ✅ STAY - version info
scripts/ ✅ STAY - build scripts
toolDefinitions.json ✅ STAY - generated file
skillDefinitions.json ✅ STAY - generated file
{
"name": "@sentry/mcp-core",
"version": "0.22.0",
"type": "module",
"private": true,
"exports": {
".": "./dist/index.js",
"./api-client": "./dist/api-client/index.js",
"./api-client/client": "./dist/api-client/client.js",
"./api-client/errors": "./dist/api-client/errors.js",
"./api-client/schema": "./dist/api-client/schema.js",
"./api-client/types": "./dist/api-client/types.js",
"./server": "./dist/server.js",
"./types": "./dist/types.js",
"./tools": "./dist/tools/index.js",
"./tools/types": "./dist/tools/types.js",
"./permissions": "./dist/permissions.js",
"./skills": "./dist/skills.js",
"./schema": "./dist/schema.js",
"./errors": "./dist/errors.js",
"./constants": "./dist/constants.js",
"./version": "./dist/version.js",
"./telem/logging": "./dist/telem/logging.js",
"./telem/sentry": "./dist/telem/sentry.js",
"./toolDefinitions": "./dist/toolDefinitions.js",
"./skillDefinitions": "./dist/skillDefinitions.js",
"./internal/tool-helpers/api-utils": "./dist/internal/tool-helpers/api-utils.js",
"./internal/error-handling": "./dist/internal/error-handling.js"
},
"files": ["./dist/*"],
"scripts": {
"build": "pnpm run generate-definitions && tsdown",
"generate-definitions": "tsx scripts/generate-definitions.ts",
"generate-otel-namespaces": "tsx scripts/generate-otel-namespaces.ts",
"measure-tokens": "tsx scripts/measure-token-cost.ts",
"validate-skills": "tsx scripts/validate-skills-mapping.ts"
},
"dependencies": {
"@ai-sdk/openai": "catalog:",
"@logtape/logtape": "catalog:",
"@logtape/sentry": "catalog:",
"@modelcontextprotocol/sdk": "catalog:",
"@sentry/core": "catalog:",
"ai": "catalog:",
"dotenv": "catalog:",
"zod": "catalog:",
"zod-to-json-schema": "catalog:"
},
"devDependencies": {
"@sentry/mcp-server-mocks": "workspace:*",
"@sentry/mcp-server-tsconfig": "workspace:*",
"@types/node": "catalog:",
"tsdown": "catalog:",
"tsx": "catalog:",
"typescript": "catalog:",
"vitest": "catalog:"
}
}Key changes from current mcp-server:
- Added
"private": true - Removed
"bin"field (not an executable) - Removed stdio-specific exports
- Kept all other exports intact
{
"name": "@sentry/mcp-server",
"version": "0.22.0",
"description": "Sentry MCP server for stdio transport",
"type": "module",
"bin": {
"sentry-mcp": "./dist/index.js"
},
"files": ["./dist/*"],
"scripts": {
"build": "tsdown",
"dev": "tsdown --watch",
"start": "tsx src/index.ts"
},
"dependencies": {
"@modelcontextprotocol/sdk": "catalog:",
"@sentry/node": "catalog:",
"@sentry/core": "catalog:",
"dotenv": "catalog:",
"zod": "catalog:"
},
"devDependencies": {
"@sentry/mcp-core": "workspace:*",
"@sentry/mcp-server-mocks": "workspace:*",
"@sentry/mcp-server-tsconfig": "workspace:*",
"@types/node": "catalog:",
"tsdown": "catalog:",
"tsx": "catalog:",
"typescript": "catalog:",
"vitest": "catalog:"
}
}Key points:
- Keeps existing name
@sentry/mcp-server(no breaking changes) - Has
binfield for CLI execution - mcp-core is devDependency (build-time only)
- Dependencies are minimal runtime-only deps
- tsdown will bundle all mcp-core code
import { defineConfig } from "tsdown";
export default defineConfig({
entry: ["src/**/*.ts", "!src/**/*.test.ts"],
format: ["cjs", "esm"],
dts: true,
sourcemap: true,
clean: true,
external: [
// Only mark test-only packages as external
"@sentry/mcp-server-mocks",
// Everything else (including @sentry/mcp-core) will be bundled
],
env: {
SENTRY_ENVIRONMENT: "stdio",
npm_package_version: "{{version}}"
}
});Critical configuration:
- Does NOT list
@sentry/mcp-coreinexternal - This means tsdown will bundle all mcp-core code into dist/
- Only mocks are external (for testing purposes)
{
"name": "@sentry/mcp-cloudflare",
"private": true,
"dependencies": {
"@sentry/mcp-core": "workspace:*"
}
}Changes:
- Replace
@sentry/mcp-server→@sentry/mcp-core - Package stays private, so workspace dependency is fine
All moved files need import updates:
// BEFORE (relative imports)
import { buildServer } from "./server";
import { startStdio } from "./transports/stdio";
import type { ServerContext } from "./types";
// AFTER (package imports)
import { buildServer } from "@sentry/mcp-core/server";
import { startStdio } from "./transports/stdio";
import type { ServerContext } from "@sentry/mcp-core/types";// BEFORE
import { LIB_VERSION } from "../version";
import type { ServerContext } from "../types";
// AFTER
import { LIB_VERSION } from "@sentry/mcp-core/version";
import type { ServerContext } from "@sentry/mcp-core/types";// BEFORE
import { ALL_SCOPES, parseScopes, expandScopes } from "../permissions";
import { parseSkills, getScopesForSkills } from "../skills";
import { DEFAULT_SCOPES, DEFAULT_SKILLS } from "../constants";
import { UserInputError } from "../errors";
// AFTER
import { ALL_SCOPES, parseScopes, expandScopes } from "@sentry/mcp-core/permissions";
import { parseSkills, getScopesForSkills } from "@sentry/mcp-core/skills";
import { DEFAULT_SCOPES, DEFAULT_SKILLS } from "@sentry/mcp-core/constants";
import { UserInputError } from "@sentry/mcp-core/errors";Update all imports from @sentry/mcp-server → @sentry/mcp-core:
// BEFORE
import { buildServer } from "@sentry/mcp-server/server";
import type { ServerContext } from "@sentry/mcp-server/types";
import { parseScopes, expandScopes } from "@sentry/mcp-server/permissions";
// AFTER
import { buildServer } from "@sentry/mcp-core/server";
import type { ServerContext } from "@sentry/mcp-core/types";
import { parseScopes, expandScopes } from "@sentry/mcp-core/permissions";Files to update in mcp-cloudflare:
src/server/index.tssrc/server/auth/verify.tssrc/server/routes/mcp.tssrc/server/sentry.config.ts- Any other files importing from mcp-server
- Rename directory:
packages/mcp-server/→packages/mcp-core/ - Update
package.json:- Change
nameto@sentry/mcp-core - Add
"private": true - Remove
binfield
- Change
- Remove stdio-specific exports from
exportsfield - Test build:
cd packages/mcp-core && pnpm build
- Create directory:
packages/mcp-stdio/ - Create initial structure:
mkdir -p packages/mcp-stdio/src/{cli,transports} - Move files from mcp-core to mcp-stdio:
src/cli/*→mcp-stdio/src/cli/src/transports/stdio.ts→mcp-stdio/src/transports/stdio.tssrc/index.ts→mcp-stdio/src/index.ts
- Create
package.json(see config above) - Create
tsdown.config.ts(see config above) - Create
tsconfig.json:{ "extends": "@sentry/mcp-server-tsconfig/base.json", "compilerOptions": { "outDir": "./dist", "rootDir": "./src" }, "include": ["src/**/*"], "exclude": ["node_modules", "dist", "**/*.test.ts"] } - Update imports in moved files (see "Import Changes" section)
- Test build:
cd packages/mcp-stdio && pnpm build - Verify bundling worked:
# Check that mcp-core code is bundled cat dist/index.js | grep "buildServer" # Should see the actual function code, not just an import
- Update
packages/mcp-cloudflare/package.json:- Change dependency:
"@sentry/mcp-server"→"@sentry/mcp-core": "workspace:*"
- Change dependency:
- Find and replace imports:
cd packages/mcp-cloudflare # Find all imports grep -r "@sentry/mcp-server" src/ # Replace (manually or with sed) find src/ -type f -name "*.ts" -exec sed -i '' 's/@sentry\/mcp-server/@sentry\/mcp-core/g' {} +
- Test build:
cd packages/mcp-cloudflare && pnpm build
- Update
packages/mcp-test-client/package.json:- If it imports from mcp-server, update to mcp-core
- Update
packages/mcp-server-evals/package.json:- Same as above
- Update
packages/mcp-server-mocks/package.json:- Check if any changes needed
- Update
pnpm-workspace.yaml(if needed):packages: - packages/*
- Update
turbo.json:- Ensure build dependencies are correct
- mcp-stdio should depend on mcp-core build
- Update root
package.jsonscripts if they reference package names
- Verify turbo cache works:
pnpm -w run build # Should build mcp-core first, then mcp-stdio - Test incremental builds:
# Change a file in mcp-core echo "// test" >> packages/mcp-core/src/server.ts pnpm -w run build # Should rebuild both mcp-core and mcp-stdio
- Update GitHub Actions workflows:
.github/workflows/test.yml.github/workflows/release.yml- Any other workflows that reference package names
- Update
CLAUDE.md:- Repository map section
- Package descriptions
- Update
docs/architecture.mdc:- Package structure
- Dependencies diagram
- Update
docs/releases/stdio.mdc:- New package structure
- Build process
- Update
docs/cursor.mdc:- Package references
- Update
AGENTS.md:- Package structure
- Update
packages/mcp-core/README.md:- Note it's a private package
- Explain it's shared code
- Update
packages/mcp-stdio/README.md:- Installation instructions
- Note it bundles core
- Update any other docs mentioning package structure
-
Build tests:
pnpm -w run build # All packages should build successfully -
Type check:
pnpm -w run tsc # No type errors -
Lint:
pnpm -w run lint # No lint errors -
Unit tests:
pnpm -w run test # All tests pass
-
Test stdio package locally:
cd packages/mcp-stdio node dist/index.js --help # Should show help text node dist/index.js --access-token=test-token --version # Should show version
-
Test with npx simulation:
cd packages/mcp-stdio pnpm pack # Creates sentry-mcp-server-*.tgz npm install -g ./sentry-mcp-server-*.tgz sentry-mcp --help # Should work globally npm uninstall -g @sentry/mcp-server
-
Test cloudflare package:
cd packages/mcp-cloudflare pnpm dev # Should start dev server without errors
-
Test MCP inspector:
pnpm inspector # Connect to stdio server # Verify tools load correctly
-
Verify no mcp-core in published package:
cd packages/mcp-stdio tar -tzf sentry-mcp-server-*.tgz # Check package.json tar -xzf sentry-mcp-server-*.tgz cat package/package.json | grep mcp-core # Should NOT appear in dependencies
-
pnpm -w run buildsucceeds - mcp-core builds first (turbo dependency)
- mcp-stdio builds after mcp-core
- mcp-cloudflare builds successfully
- All dist/ directories contain expected files
- mcp-stdio/dist/index.js contains bundled mcp-core code
- mcp-stdio/dist/index.js does NOT import from @sentry/mcp-core
- File size is reasonable (check
ls -lh packages/mcp-stdio/dist/)
- mcp-core has
"private": true - mcp-core has no
binfield - mcp-stdio has
binfield pointing to dist/index.js - mcp-stdio has mcp-core in devDependencies only
- Packed package.json has no mcp-core reference
- All mcp-stdio files import from
@sentry/mcp-core/* - All mcp-cloudflare files import from
@sentry/mcp-core/* - No relative imports crossing package boundaries
- All unit tests pass
- CLI tests work in mcp-stdio package
- No broken imports in test files
- Mock imports still work
-
node packages/mcp-stdio/dist/index.js --helpworks -
node packages/mcp-stdio/dist/index.js --versionworks - Can connect via MCP inspector
- Tools load and execute correctly
- Cloudflare dev server starts
- All docs updated with new package names
- Installation instructions correct
- Architecture diagrams updated
- No broken doc links
If issues arise during implementation:
- Before committing: Simply delete mcp-stdio, rename mcp-core back to mcp-server
- After committing: Revert the commit:
git revert <commit-hash> - If published: Publish a new patch version with the old structure
- ✅ No breaking changes (package name stays
@sentry/mcp-server) - ✅ Same installation:
npx @sentry/mcp-server - ✅ Same CLI flags and behavior
- ✅ No knowledge of mcp-core needed
- ✅ Clear separation: core vs transport-specific code
- ✅ Cloudflare can depend on core directly
- ✅ Better modularity for future transports
- ✅ Type safety during development
- ✅ Turbo caching works correctly
- ✅ mcp-core is private (never published)
- ✅ mcp-stdio fully self-contained (no external core dep)
- ✅ Clean package boundaries
- ✅ Easier to maintain and extend
- ✅ Future HTTP/SSE transports can be separate packages
If we want to add other transports in the future:
packages/
├── mcp-core/ # Shared code (private)
├── mcp-stdio/ # Stdio transport (published as @sentry/mcp-server)
├── mcp-http/ # Future: HTTP transport package
├── mcp-sse/ # Future: SSE transport package
└── mcp-cloudflare/ # Web app (uses core via workspace)
Each transport package would:
- Import from
@sentry/mcp-coreas devDependency - Bundle all core code at build time
- Be published independently with its own name
- Have transport-specific configuration and entry points
- Type safety: Full IDE support during development
- Clean publishing: No reference to unpublished package
- Turbo-friendly: Explicit build dependencies
- User-friendly: Zero knowledge of internal packages needed
- No breaking changes for existing users
- Existing documentation remains valid
- npx commands don't need updating
- IDE configs don't need updating
- It's an internal implementation detail
- No use case for external consumption
- Reduces maintenance burden (no semver, no changelog)
- Allows internal refactoring without breaking changes
Q: Why not use tsup instead of tsdown? A: tsdown is already used and works well. No need to change.
Q: Should we bundle node_modules too? A: No, only mcp-core. External packages like MCP SDK and Sentry should remain external.
Q: What about source maps? A: Enable in tsdown config for better debugging.
Q: How to handle version bumps? A: Both packages should have same version. Consider using Changesets or manual sync.
Q: What about the README? A: mcp-stdio keeps the user-facing README. mcp-core gets a developer-focused README.
- Phase 1-2 (Core rename + Stdio creation): 2-3 hours
- Phase 3-4 (Update other packages): 1 hour
- Phase 5-6 (Build pipeline): 1 hour
- Phase 7 (Documentation): 1-2 hours
- Phase 8 (Testing): 2-3 hours
Total: ~8-12 hours of focused work
The refactoring is complete when:
- ✅
pnpm -w run buildsucceeds for all packages - ✅
pnpm -w run testpasses all tests - ✅ Can run stdio server:
node packages/mcp-stdio/dist/index.js --help - ✅ Can pack and install:
npm install -g ./packages/mcp-stdio/*.tgz - ✅ Cloudflare dev server works:
cd packages/mcp-cloudflare && pnpm dev - ✅ No
@sentry/mcp-corein packed mcp-stdio package.json - ✅ All documentation updated and accurate
End of Plan