@hachej/boring-mcp 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,213 @@
1
+ declare const BORING_MCP_PLUGIN_ID = "boring-mcp";
2
+ declare const BORING_MCP_SOURCES_TAB_PANEL_ID = "boring-mcp.sources.tab";
3
+ declare const BORING_MCP_SOURCES_PANEL_ID = "boring-mcp.sources.panel";
4
+ declare const MCP_ERROR_CODES: {
5
+ readonly SOURCE_NOT_FOUND: "MCP_SOURCE_NOT_FOUND";
6
+ readonly SOURCE_FORBIDDEN: "MCP_SOURCE_FORBIDDEN";
7
+ readonly SOURCE_UNAVAILABLE: "MCP_SOURCE_UNAVAILABLE";
8
+ readonly PROVIDER_CONFIG_INVALID: "MCP_PROVIDER_CONFIG_INVALID";
9
+ readonly PROVIDER_TIMEOUT: "MCP_PROVIDER_TIMEOUT";
10
+ readonly PROVIDER_ERROR: "MCP_PROVIDER_ERROR";
11
+ readonly TOOL_NOT_FOUND: "MCP_TOOL_NOT_FOUND";
12
+ readonly TOOL_NOT_ALLOWED: "MCP_TOOL_NOT_ALLOWED";
13
+ readonly PROVIDER_TOOL_DRIFT: "MCP_PROVIDER_TOOL_DRIFT";
14
+ readonly RESOURCE_LIMIT_EXCEEDED: "MCP_RESOURCE_LIMIT_EXCEEDED";
15
+ readonly SECRET_LEAK_GUARD: "MCP_SECRET_LEAK_GUARD";
16
+ readonly INPUT_INVALID: "MCP_INPUT_INVALID";
17
+ readonly RESOURCE_URI_INVALID: "MCP_RESOURCE_URI_INVALID";
18
+ };
19
+ type McpErrorCode = (typeof MCP_ERROR_CODES)[keyof typeof MCP_ERROR_CODES];
20
+ type McpProviderId = "notion" | "airtable" | (string & {});
21
+ type McpTransport = "streamable-http";
22
+ type McpSourceStatus = "connected" | "expired" | "revoked" | "error" | "unconfigured";
23
+ type McpToolRisk = "read" | "write" | "admin" | "unknown";
24
+ type McpCredentialProvider = "provider-managed" | "composio-managed" | "app-managed" | "user-managed" | (string & {});
25
+ type McpSourceOwnerKind = "user" | "company_context" | "team_context" | "project_context";
26
+ interface McpActor {
27
+ userId: string;
28
+ workspaceId: string;
29
+ isAdmin?: boolean;
30
+ }
31
+ interface McpProviderTemplate {
32
+ id: McpProviderId;
33
+ displayName: string;
34
+ endpoint?: string;
35
+ transport?: McpTransport;
36
+ readOnlyDefault: boolean;
37
+ allowedTools: string[];
38
+ deniedTools: string[];
39
+ allowedResourceUriPrefixes?: string[];
40
+ }
41
+ interface McpConnectorRef {
42
+ provider: McpProviderId;
43
+ toolkitId?: string;
44
+ externalSourceId?: string;
45
+ connectedAccountId?: string;
46
+ sessionId?: string;
47
+ }
48
+ interface McpSource {
49
+ id: string;
50
+ workspaceId: string;
51
+ userId: string;
52
+ provider: McpProviderId;
53
+ displayName: string;
54
+ status: McpSourceStatus;
55
+ ownerKind: McpSourceOwnerKind;
56
+ credentialProvider: McpCredentialProvider;
57
+ scopes?: string[];
58
+ providerAccountLabel?: string;
59
+ connectorRef?: McpConnectorRef;
60
+ lastVerifiedAt?: string;
61
+ createdAt?: string;
62
+ updatedAt?: string;
63
+ }
64
+ type McpSourceDto = Pick<McpSource, "id" | "provider" | "displayName" | "status" | "ownerKind" | "credentialProvider" | "scopes" | "providerAccountLabel" | "lastVerifiedAt" | "createdAt" | "updatedAt">;
65
+ interface McpDiscoveredTool {
66
+ name: string;
67
+ description?: string;
68
+ inputSchema?: unknown;
69
+ }
70
+ interface McpDiscoveredResource {
71
+ uri: string;
72
+ name?: string;
73
+ description?: string;
74
+ mimeType?: string;
75
+ }
76
+ interface McpToolDecision {
77
+ allowed: boolean;
78
+ risk: McpToolRisk;
79
+ reason: string;
80
+ }
81
+ interface McpProbeResult {
82
+ sourceId: string;
83
+ provider: McpProviderId;
84
+ tools: Array<McpDiscoveredTool & {
85
+ decision: McpToolDecision;
86
+ }>;
87
+ resources: McpDiscoveredResource[];
88
+ }
89
+ interface McpDoctorIssue {
90
+ level: "error" | "warning";
91
+ code: McpErrorCode;
92
+ message: string;
93
+ }
94
+ interface McpDoctorResult {
95
+ ok: boolean;
96
+ sourceId: string;
97
+ issues: McpDoctorIssue[];
98
+ }
99
+ interface McpToolCatalogEntry {
100
+ sourceId: string;
101
+ provider: McpProviderId;
102
+ toolName: string;
103
+ displayName: string;
104
+ summary: string;
105
+ description?: string;
106
+ inputSchema: unknown;
107
+ outputSchema?: unknown;
108
+ risk: McpToolRisk;
109
+ enabled: boolean;
110
+ blockedReasons: string[];
111
+ schemaHash: string;
112
+ nativeRef: {
113
+ provider: string;
114
+ toolkit?: string;
115
+ action: string;
116
+ };
117
+ }
118
+ type NormalizedMcpTool = McpToolCatalogEntry;
119
+ interface McpToolSearchResult {
120
+ tools: McpToolCatalogEntry[];
121
+ }
122
+ interface McpToolDescribeResult {
123
+ tool: McpToolCatalogEntry;
124
+ schemaDrifted: boolean;
125
+ }
126
+ interface McpReadonlyCallInput {
127
+ sourceId: string;
128
+ toolName: string;
129
+ input?: unknown;
130
+ expectedSchemaHash?: string;
131
+ }
132
+ interface McpReadonlyCallResult {
133
+ content: unknown;
134
+ }
135
+ interface McpReadonlyCallAuditEvent {
136
+ operation: "mcp_readonly_call";
137
+ outcome: "success" | "blocked" | "failure";
138
+ workspaceId: string;
139
+ userId: string;
140
+ sourceId: string;
141
+ toolName: string;
142
+ expectedSchemaHash?: string;
143
+ code?: string;
144
+ }
145
+ interface McpToolCallResult {
146
+ content: unknown;
147
+ }
148
+ interface McpSourceStore {
149
+ listSources(actor: McpActor): Promise<McpSource[]>;
150
+ getSource(sourceId: string): Promise<McpSource | undefined>;
151
+ }
152
+ interface McpSourceRegistry extends McpSourceStore {
153
+ disconnectSource?(actor: McpActor, sourceId: string): Promise<McpSource | undefined>;
154
+ }
155
+ interface McpSourceStatusPayload {
156
+ source: McpSourceDto;
157
+ connectable: boolean;
158
+ canProbe: boolean;
159
+ canDisconnect: boolean;
160
+ }
161
+ declare function toMcpSourceDto(source: McpSource): McpSourceDto;
162
+ interface McpTransportListToolsOptions {
163
+ /** Force bypassing provider-level metadata caches, not just local catalog caches. */
164
+ forceProviderRefresh?: boolean;
165
+ }
166
+ interface McpTransportClient {
167
+ listTools(source: McpSource, options?: McpTransportListToolsOptions): Promise<McpDiscoveredTool[]>;
168
+ listResources(source: McpSource): Promise<McpDiscoveredResource[]>;
169
+ readResource(source: McpSource, uri: string): Promise<unknown>;
170
+ callTool(source: McpSource, toolName: string, input: unknown): Promise<McpToolCallResult>;
171
+ }
172
+ interface McpSourceAccessPolicy {
173
+ canAccessSource(actor: McpActor, source: McpSource): boolean;
174
+ }
175
+ declare class McpError extends Error {
176
+ readonly code: McpErrorCode;
177
+ readonly details: unknown;
178
+ constructor(code: McpErrorCode, message: string, details?: unknown);
179
+ }
180
+ declare const NOTION_MCP_TEMPLATE: McpProviderTemplate;
181
+ declare const AIRTABLE_MCP_TEMPLATE: McpProviderTemplate;
182
+ declare const DEFAULT_MCP_PROVIDER_TEMPLATES: readonly [McpProviderTemplate, McpProviderTemplate];
183
+ declare function getMcpProviderTemplate(provider: string, templates?: readonly McpProviderTemplate[]): McpProviderTemplate | undefined;
184
+ declare const MCP_TOOL_NAME_PATTERN: RegExp;
185
+ declare function validateMcpToolName(toolName: string): void;
186
+ declare function classifyMcpTool(template: McpProviderTemplate, toolName: string): McpToolDecision;
187
+ declare function classifyMcpTools(template: McpProviderTemplate, tools: readonly McpDiscoveredTool[]): Array<McpDiscoveredTool & {
188
+ decision: McpToolDecision;
189
+ }>;
190
+ declare function assertMcpToolAllowed(template: McpProviderTemplate, toolName: string): void;
191
+ declare function redactMcpSecrets(value: unknown): unknown;
192
+ declare function containsMcpSecret(value: unknown): boolean;
193
+ declare function containsMcpCanary(value: unknown, canaries: readonly string[]): boolean;
194
+ declare function containsMcpSecretOrCanary(value: unknown, canaries: readonly string[]): boolean;
195
+ declare function doctorMcpSource(source: McpSource, templates?: readonly McpProviderTemplate[]): McpDoctorResult;
196
+ declare class McpAccessFacade {
197
+ private readonly params;
198
+ constructor(params: {
199
+ store: McpSourceStore;
200
+ transport: McpTransportClient;
201
+ templates?: readonly McpProviderTemplate[];
202
+ maxInputBytes?: number;
203
+ accessPolicy?: McpSourceAccessPolicy;
204
+ });
205
+ listSources(actor: McpActor): Promise<McpSource[]>;
206
+ probeSource(actor: McpActor, sourceId: string): Promise<McpProbeResult>;
207
+ private requireAccessibleSource;
208
+ private canAccessSource;
209
+ private requireConnectedSource;
210
+ private requireTemplate;
211
+ }
212
+
213
+ export { AIRTABLE_MCP_TEMPLATE, BORING_MCP_PLUGIN_ID, BORING_MCP_SOURCES_PANEL_ID, BORING_MCP_SOURCES_TAB_PANEL_ID, DEFAULT_MCP_PROVIDER_TEMPLATES, MCP_ERROR_CODES, MCP_TOOL_NAME_PATTERN, McpAccessFacade, type McpActor, type McpConnectorRef, type McpCredentialProvider, type McpDiscoveredResource, type McpDiscoveredTool, type McpDoctorIssue, type McpDoctorResult, McpError, type McpErrorCode, type McpProbeResult, type McpProviderId, type McpProviderTemplate, type McpReadonlyCallAuditEvent, type McpReadonlyCallInput, type McpReadonlyCallResult, type McpSource, type McpSourceAccessPolicy, type McpSourceDto, type McpSourceOwnerKind, type McpSourceRegistry, type McpSourceStatus, type McpSourceStatusPayload, type McpSourceStore, type McpToolCallResult, type McpToolCatalogEntry, type McpToolDecision, type McpToolDescribeResult, type McpToolRisk, type McpToolSearchResult, type McpTransport, type McpTransportClient, type McpTransportListToolsOptions, NOTION_MCP_TEMPLATE, type NormalizedMcpTool, assertMcpToolAllowed, classifyMcpTool, classifyMcpTools, containsMcpCanary, containsMcpSecret, containsMcpSecretOrCanary, doctorMcpSource, getMcpProviderTemplate, redactMcpSecrets, toMcpSourceDto, validateMcpToolName };
@@ -0,0 +1,194 @@
1
+ // src/shared/index.ts
2
+ var BORING_MCP_PLUGIN_ID = "boring-mcp";
3
+ var BORING_MCP_SOURCES_TAB_PANEL_ID = "boring-mcp.sources.tab";
4
+ var BORING_MCP_SOURCES_PANEL_ID = "boring-mcp.sources.panel";
5
+ var MCP_ERROR_CODES = {
6
+ SOURCE_NOT_FOUND: "MCP_SOURCE_NOT_FOUND",
7
+ SOURCE_FORBIDDEN: "MCP_SOURCE_FORBIDDEN",
8
+ SOURCE_UNAVAILABLE: "MCP_SOURCE_UNAVAILABLE",
9
+ PROVIDER_CONFIG_INVALID: "MCP_PROVIDER_CONFIG_INVALID",
10
+ PROVIDER_TIMEOUT: "MCP_PROVIDER_TIMEOUT",
11
+ PROVIDER_ERROR: "MCP_PROVIDER_ERROR",
12
+ TOOL_NOT_FOUND: "MCP_TOOL_NOT_FOUND",
13
+ TOOL_NOT_ALLOWED: "MCP_TOOL_NOT_ALLOWED",
14
+ PROVIDER_TOOL_DRIFT: "MCP_PROVIDER_TOOL_DRIFT",
15
+ RESOURCE_LIMIT_EXCEEDED: "MCP_RESOURCE_LIMIT_EXCEEDED",
16
+ SECRET_LEAK_GUARD: "MCP_SECRET_LEAK_GUARD",
17
+ INPUT_INVALID: "MCP_INPUT_INVALID",
18
+ RESOURCE_URI_INVALID: "MCP_RESOURCE_URI_INVALID"
19
+ };
20
+ function toMcpSourceDto(source) {
21
+ return {
22
+ id: source.id,
23
+ provider: source.provider,
24
+ displayName: source.displayName,
25
+ status: source.status,
26
+ ownerKind: source.ownerKind,
27
+ credentialProvider: source.credentialProvider,
28
+ scopes: source.scopes,
29
+ providerAccountLabel: source.providerAccountLabel,
30
+ lastVerifiedAt: source.lastVerifiedAt,
31
+ createdAt: source.createdAt,
32
+ updatedAt: source.updatedAt
33
+ };
34
+ }
35
+ var McpError = class extends Error {
36
+ code;
37
+ details;
38
+ constructor(code, message, details) {
39
+ super(message);
40
+ this.name = "McpError";
41
+ this.code = code;
42
+ this.details = details;
43
+ }
44
+ };
45
+ var NOTION_MCP_TEMPLATE = {
46
+ id: "notion",
47
+ displayName: "Notion",
48
+ readOnlyDefault: true,
49
+ allowedTools: ["NOTION_SEARCH_NOTION_PAGE", "NOTION_GET_PAGE_MARKDOWN", "NOTION_RETRIEVE_PAGE"],
50
+ deniedTools: ["create_*", "update_*", "delete_*", "publish_*", "admin_*"],
51
+ allowedResourceUriPrefixes: ["notion:", "notion://"]
52
+ };
53
+ var AIRTABLE_MCP_TEMPLATE = {
54
+ id: "airtable",
55
+ displayName: "Airtable",
56
+ readOnlyDefault: true,
57
+ allowedTools: ["ping", "list_bases", "list_workspaces", "list_tables_for_base", "get_table_schema", "search_records"],
58
+ deniedTools: ["create_*", "update_*", "delete_*", "publish_*", "admin_*"],
59
+ allowedResourceUriPrefixes: ["airtable:", "airtable://"]
60
+ };
61
+ var DEFAULT_MCP_PROVIDER_TEMPLATES = [NOTION_MCP_TEMPLATE, AIRTABLE_MCP_TEMPLATE];
62
+ function getMcpProviderTemplate(provider, templates = DEFAULT_MCP_PROVIDER_TEMPLATES) {
63
+ return templates.find((template) => template.id === provider);
64
+ }
65
+ var MCP_TOOL_NAME_PATTERN = /^[A-Za-z0-9_.:-]{1,128}$/;
66
+ function validateMcpToolName(toolName) {
67
+ if (!MCP_TOOL_NAME_PATTERN.test(toolName)) {
68
+ throw new McpError(MCP_ERROR_CODES.INPUT_INVALID, "Invalid MCP tool name");
69
+ }
70
+ }
71
+ function wildcardMatch(pattern, value) {
72
+ if (!pattern.includes("*")) return pattern === value;
73
+ const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
74
+ return new RegExp(`^${escaped}$`, "i").test(value);
75
+ }
76
+ function classifyMcpTool(template, toolName) {
77
+ validateMcpToolName(toolName);
78
+ if (template.deniedTools.some((pattern) => wildcardMatch(pattern, toolName))) {
79
+ return { allowed: false, risk: "write", reason: "Tool matches a denied write/admin pattern" };
80
+ }
81
+ if (template.allowedTools.some((pattern) => wildcardMatch(pattern, toolName))) {
82
+ return { allowed: true, risk: "read", reason: "Tool is on the read-only allowlist" };
83
+ }
84
+ return { allowed: false, risk: "unknown", reason: "Tool is not on the read-only allowlist" };
85
+ }
86
+ function classifyMcpTools(template, tools) {
87
+ return tools.map((tool) => ({ ...tool, decision: classifyMcpTool(template, tool.name) }));
88
+ }
89
+ function assertMcpToolAllowed(template, toolName) {
90
+ const decision = classifyMcpTool(template, toolName);
91
+ if (!decision.allowed) throw new McpError(MCP_ERROR_CODES.TOOL_NOT_ALLOWED, decision.reason);
92
+ }
93
+ var REDACTION = "[REDACTED_MCP_SECRET]";
94
+ var SECRET_KEY_PATTERN = /(api[_-]?key|access[_-]?token|refresh[_-]?token|id[_-]?token|oauth[_-]?token|authorization|cookie|client[_-]?secret|session[_-]?headers?|mcp[_-]?session|x-composio-mcp-session)/i;
95
+ var SECRET_VALUE_PATTERN = /(Bearer\s+[A-Za-z0-9._~+\/-]{12,}|sk-[A-Za-z0-9_-]{12,}|(?:x-api-key|api[_-]?key|access[_-]?token|refresh[_-]?token|id[_-]?token|oauth[_-]?token|code|client[_-]?secret|session[_-]?headers?|mcp[_-]?session|x-composio-mcp-session)\s*[:=]\s*[^\s,&,}]+)/gi;
96
+ function redactMcpSecrets(value) {
97
+ if (typeof value === "string") return value.replace(SECRET_VALUE_PATTERN, REDACTION);
98
+ if (Array.isArray(value)) return value.map(redactMcpSecrets);
99
+ if (!value || typeof value !== "object") return value;
100
+ return Object.fromEntries(Object.entries(value).map(([key, nested]) => [key, SECRET_KEY_PATTERN.test(key) ? REDACTION : redactMcpSecrets(nested)]));
101
+ }
102
+ function containsMcpSecret(value) {
103
+ const redacted = redactMcpSecrets(value);
104
+ return JSON.stringify(redacted) !== JSON.stringify(value);
105
+ }
106
+ function hasMcpCanaryText(value, canaries) {
107
+ return canaries.some((canary) => canary.trim() && value.includes(canary));
108
+ }
109
+ function containsMcpCanary(value, canaries) {
110
+ if (typeof value === "string") return hasMcpCanaryText(value, canaries);
111
+ if (Array.isArray(value)) return value.some((item) => containsMcpCanary(item, canaries));
112
+ if (!value || typeof value !== "object") return false;
113
+ return Object.entries(value).some(([key, nested]) => hasMcpCanaryText(key, canaries) || containsMcpCanary(nested, canaries));
114
+ }
115
+ function containsMcpSecretOrCanary(value, canaries) {
116
+ return containsMcpSecret(value) || containsMcpCanary(value, canaries);
117
+ }
118
+ function doctorMcpSource(source, templates = DEFAULT_MCP_PROVIDER_TEMPLATES) {
119
+ const issues = [];
120
+ if (!getMcpProviderTemplate(source.provider, templates)) {
121
+ issues.push({ level: "error", code: MCP_ERROR_CODES.PROVIDER_CONFIG_INVALID, message: "Unknown MCP provider template" });
122
+ }
123
+ if (source.status !== "connected") {
124
+ issues.push({ level: "warning", code: MCP_ERROR_CODES.SOURCE_UNAVAILABLE, message: "MCP source is not connected" });
125
+ }
126
+ return { ok: issues.every((issue) => issue.level !== "error"), sourceId: source.id, issues };
127
+ }
128
+ var McpAccessFacade = class {
129
+ constructor(params) {
130
+ this.params = params;
131
+ }
132
+ params;
133
+ async listSources(actor) {
134
+ return (await this.params.store.listSources(actor)).filter((source) => this.canAccessSource(actor, source));
135
+ }
136
+ async probeSource(actor, sourceId) {
137
+ const source = await this.requireAccessibleSource(actor, sourceId);
138
+ this.requireConnectedSource(source);
139
+ const template = this.requireTemplate(source);
140
+ const [tools, resources] = await Promise.all([
141
+ this.params.transport.listTools(source),
142
+ this.params.transport.listResources(source)
143
+ ]);
144
+ return {
145
+ sourceId: source.id,
146
+ provider: source.provider,
147
+ tools: classifyMcpTools(template, tools),
148
+ resources
149
+ };
150
+ }
151
+ async requireAccessibleSource(actor, sourceId) {
152
+ const source = await this.params.store.getSource(sourceId);
153
+ if (!source || source.workspaceId !== actor.workspaceId) {
154
+ throw new McpError(MCP_ERROR_CODES.SOURCE_NOT_FOUND, "MCP source not found");
155
+ }
156
+ if (!this.canAccessSource(actor, source)) throw new McpError(MCP_ERROR_CODES.SOURCE_NOT_FOUND, "MCP source not found");
157
+ return source;
158
+ }
159
+ canAccessSource(actor, source) {
160
+ if (source.workspaceId !== actor.workspaceId) return false;
161
+ return this.params.accessPolicy?.canAccessSource(actor, source) ?? (source.ownerKind === "user" && source.userId === actor.userId);
162
+ }
163
+ requireConnectedSource(source) {
164
+ if (source.status !== "connected") throw new McpError(MCP_ERROR_CODES.SOURCE_UNAVAILABLE, "MCP source is not connected");
165
+ }
166
+ requireTemplate(source) {
167
+ const template = getMcpProviderTemplate(source.provider, this.params.templates);
168
+ if (!template) throw new McpError(MCP_ERROR_CODES.PROVIDER_CONFIG_INVALID, "Unknown MCP provider");
169
+ return template;
170
+ }
171
+ };
172
+ export {
173
+ AIRTABLE_MCP_TEMPLATE,
174
+ BORING_MCP_PLUGIN_ID,
175
+ BORING_MCP_SOURCES_PANEL_ID,
176
+ BORING_MCP_SOURCES_TAB_PANEL_ID,
177
+ DEFAULT_MCP_PROVIDER_TEMPLATES,
178
+ MCP_ERROR_CODES,
179
+ MCP_TOOL_NAME_PATTERN,
180
+ McpAccessFacade,
181
+ McpError,
182
+ NOTION_MCP_TEMPLATE,
183
+ assertMcpToolAllowed,
184
+ classifyMcpTool,
185
+ classifyMcpTools,
186
+ containsMcpCanary,
187
+ containsMcpSecret,
188
+ containsMcpSecretOrCanary,
189
+ doctorMcpSource,
190
+ getMcpProviderTemplate,
191
+ redactMcpSecrets,
192
+ toMcpSourceDto,
193
+ validateMcpToolName
194
+ };
package/package.json ADDED
@@ -0,0 +1,78 @@
1
+ {
2
+ "name": "@hachej/boring-mcp",
3
+ "version": "0.0.0",
4
+ "type": "module",
5
+ "private": false,
6
+ "license": "MIT",
7
+ "publishConfig": {
8
+ "access": "public"
9
+ },
10
+ "description": "Reusable boring-ui MCP plugin foundation: generic MCP UI, policy, redaction, and facade contracts.",
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "https://github.com/hachej/boring-ui"
14
+ },
15
+ "homepage": "https://github.com/hachej/boring-ui",
16
+ "boring": {
17
+ "id": "boring-mcp",
18
+ "label": "MCP",
19
+ "front": "dist/front/index.js",
20
+ "server": "dist/server/index.js"
21
+ },
22
+ "pi": {
23
+ "systemPrompt": "Use boring-mcp bridge tools only when an app has enabled them. Treat MCP sources as read-only unless a tool is explicitly allowed."
24
+ },
25
+ "files": [
26
+ "dist"
27
+ ],
28
+ "exports": {
29
+ ".": {
30
+ "types": "./dist/front/index.d.ts",
31
+ "import": "./dist/front/index.js"
32
+ },
33
+ "./front": {
34
+ "types": "./dist/front/index.d.ts",
35
+ "import": "./dist/front/index.js"
36
+ },
37
+ "./server": {
38
+ "types": "./dist/server/index.d.ts",
39
+ "import": "./dist/server/index.js"
40
+ },
41
+ "./shared": {
42
+ "types": "./dist/shared/index.d.ts",
43
+ "import": "./dist/shared/index.js"
44
+ },
45
+ "./package.json": "./package.json"
46
+ },
47
+ "sideEffects": false,
48
+ "scripts": {
49
+ "build": "tsup",
50
+ "typecheck": "tsc --noEmit",
51
+ "test": "vitest run --no-file-parallelism",
52
+ "lint": "pnpm run typecheck",
53
+ "clean": "rm -rf dist .tsbuildinfo"
54
+ },
55
+ "peerDependencies": {
56
+ "@hachej/boring-workspace": "workspace:*",
57
+ "react": "^18.0.0 || ^19.0.0",
58
+ "react-dom": "^18.0.0 || ^19.0.0"
59
+ },
60
+ "devDependencies": {
61
+ "@hachej/boring-workspace": "workspace:*",
62
+ "@testing-library/jest-dom": "^6.9.1",
63
+ "@testing-library/react": "^16.3.0",
64
+ "@types/node": "^22.20.0",
65
+ "@types/react": "^19.2.17",
66
+ "@types/react-dom": "^19.0.0",
67
+ "@vitejs/plugin-react": "^4.0.0",
68
+ "jsdom": "^29.1.1",
69
+ "react": "^19.2.7",
70
+ "react-dom": "^19.2.7",
71
+ "tsup": "^8.4.0",
72
+ "typescript": "~6.0.3",
73
+ "vitest": "^4.1.9"
74
+ },
75
+ "dependencies": {
76
+ "@modelcontextprotocol/sdk": "^1.29.0"
77
+ }
78
+ }