@mcp-ts/sdk 2.3.4 → 2.4.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.
- package/README.md +113 -30
- package/dist/adapters/agui-adapter.d.mts +3 -3
- package/dist/adapters/agui-adapter.d.ts +3 -3
- package/dist/adapters/agui-middleware.d.mts +3 -3
- package/dist/adapters/agui-middleware.d.ts +3 -3
- package/dist/adapters/ai-adapter.d.mts +3 -3
- package/dist/adapters/ai-adapter.d.ts +3 -3
- package/dist/adapters/langchain-adapter.d.mts +3 -3
- package/dist/adapters/langchain-adapter.d.ts +3 -3
- package/dist/adapters/mastra-adapter.d.mts +1 -1
- package/dist/adapters/mastra-adapter.d.ts +1 -1
- package/dist/client/index.d.mts +2 -2
- package/dist/client/index.d.ts +2 -2
- package/dist/client/index.js +2 -2
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +2 -2
- package/dist/client/index.mjs.map +1 -1
- package/dist/client/react.d.mts +9 -8
- package/dist/client/react.d.ts +9 -8
- package/dist/client/react.js +66 -26
- package/dist/client/react.js.map +1 -1
- package/dist/client/react.mjs +67 -27
- package/dist/client/react.mjs.map +1 -1
- package/dist/client/vue.d.mts +6 -5
- package/dist/client/vue.d.ts +6 -5
- package/dist/client/vue.js +27 -17
- package/dist/client/vue.js.map +1 -1
- package/dist/client/vue.mjs +27 -17
- package/dist/client/vue.mjs.map +1 -1
- package/dist/{index-Cfjsme-a.d.mts → index-ByIjEReo.d.mts} +2 -2
- package/dist/{index-CmjMd2ac.d.ts → index-CtXvKl8N.d.ts} +2 -2
- package/dist/index.d.mts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.js +728 -306
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +722 -304
- package/dist/index.mjs.map +1 -1
- package/dist/{multi-session-client-C7hGqzgM.d.mts → multi-session-client-CIMUGF8S.d.mts} +15 -33
- package/dist/{multi-session-client-C_kPHpD5.d.ts → multi-session-client-CnvZEGPY.d.ts} +15 -33
- package/dist/server/index.d.mts +45 -17
- package/dist/server/index.d.ts +45 -17
- package/dist/server/index.js +720 -300
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +716 -299
- package/dist/server/index.mjs.map +1 -1
- package/dist/shared/index.d.mts +9 -8
- package/dist/shared/index.d.ts +9 -8
- package/dist/shared/index.js +7 -5
- package/dist/shared/index.js.map +1 -1
- package/dist/shared/index.mjs +5 -4
- package/dist/shared/index.mjs.map +1 -1
- package/dist/{tool-router-BMzhoNYt.d.ts → tool-router-CZMrOG8J.d.ts} +1 -1
- package/dist/{tool-router-BhHsvBfP.d.mts → tool-router-CuApsDiV.d.mts} +1 -1
- package/dist/{types-CxFaaZrM.d.mts → types-DCk_IF4L.d.mts} +5 -3
- package/dist/{types-CxFaaZrM.d.ts → types-DCk_IF4L.d.ts} +5 -3
- package/migrations/neon/20260513010000_install_mcp_sessions.sql +40 -3
- package/migrations/neon/20260513020000_add_session_cleanup_cron.sql +4 -4
- package/migrations/supabase/20260330195700_install_mcp_sessions.sql +67 -3
- package/migrations/supabase/20260421010000_add_session_cleanup_cron.sql +6 -6
- package/package.json +13 -3
- package/src/client/core/sse-client.ts +2 -2
- package/src/client/react/index.ts +1 -1
- package/src/client/react/oauth-popup.tsx +34 -13
- package/src/client/react/use-mcp.ts +19 -45
- package/src/client/utils/session-state.ts +43 -0
- package/src/client/vue/use-mcp.ts +18 -47
- package/src/server/handlers/sse-handler.ts +9 -4
- package/src/server/mcp/multi-session-client.ts +2 -6
- package/src/server/mcp/oauth-client.ts +73 -101
- package/src/server/mcp/storage-oauth-provider.ts +79 -50
- package/src/server/storage/file-backend.ts +74 -18
- package/src/server/storage/index.ts +2 -4
- package/src/server/storage/memory-backend.ts +49 -13
- package/src/server/storage/neon-backend.ts +201 -68
- package/src/server/storage/redis-backend.ts +81 -23
- package/src/server/storage/session-lifecycle.ts +78 -0
- package/src/server/storage/sqlite-backend.ts +89 -15
- package/src/server/storage/supabase-backend.ts +188 -63
- package/src/server/storage/types.ts +49 -16
- package/src/shared/constants.ts +5 -6
- package/src/shared/types.ts +5 -3
- package/src/shared/utils.ts +26 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Tool } from '@modelcontextprotocol/sdk/types.js';
|
|
2
|
-
import { u as ToolClientProvider, T as ToolClient } from './types-
|
|
2
|
+
import { u as ToolClientProvider, T as ToolClient } from './types-DCk_IF4L.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* ToolIndex — Lightweight in-memory search index for MCP tool discovery.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Tool } from '@modelcontextprotocol/sdk/types.js';
|
|
2
|
-
import { u as ToolClientProvider, T as ToolClient } from './types-
|
|
2
|
+
import { u as ToolClientProvider, T as ToolClient } from './types-DCk_IF4L.mjs';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* ToolIndex — Lightweight in-memory search index for MCP tool discovery.
|
|
@@ -83,6 +83,7 @@ type ToolInfo = {
|
|
|
83
83
|
outputSchema?: Tool['outputSchema'];
|
|
84
84
|
};
|
|
85
85
|
type TransportType = 'sse' | 'streamable-http';
|
|
86
|
+
type SessionStatus = 'pending' | 'active';
|
|
86
87
|
type McpRpcMethod = 'connect' | 'disconnect' | 'listTools' | 'callTool' | 'listSessions' | 'getSession' | 'finishAuth' | 'listPrompts' | 'getPrompt' | 'listResources' | 'readResource';
|
|
87
88
|
interface McpRpcRequest {
|
|
88
89
|
id: string;
|
|
@@ -126,7 +127,7 @@ interface ReadResourceParams {
|
|
|
126
127
|
uri: string;
|
|
127
128
|
}
|
|
128
129
|
interface FinishAuthParams {
|
|
129
|
-
|
|
130
|
+
state: string;
|
|
130
131
|
code: string;
|
|
131
132
|
}
|
|
132
133
|
type McpRpcParams = ConnectParams | DisconnectParams | SessionParams | CallToolParams | GetPromptParams | ReadResourceParams | FinishAuthParams | undefined;
|
|
@@ -137,11 +138,12 @@ interface SessionInfo {
|
|
|
137
138
|
serverUrl: string;
|
|
138
139
|
transport: TransportType;
|
|
139
140
|
createdAt: number;
|
|
141
|
+
updatedAt?: number;
|
|
140
142
|
/**
|
|
141
143
|
* Session readiness for auto-restore.
|
|
142
|
-
*
|
|
144
|
+
* `pending` means auth is in progress and should be resumed explicitly by user action.
|
|
143
145
|
*/
|
|
144
|
-
|
|
146
|
+
status: SessionStatus;
|
|
145
147
|
}
|
|
146
148
|
interface SessionListResult {
|
|
147
149
|
sessions: SessionInfo[];
|
|
@@ -83,6 +83,7 @@ type ToolInfo = {
|
|
|
83
83
|
outputSchema?: Tool['outputSchema'];
|
|
84
84
|
};
|
|
85
85
|
type TransportType = 'sse' | 'streamable-http';
|
|
86
|
+
type SessionStatus = 'pending' | 'active';
|
|
86
87
|
type McpRpcMethod = 'connect' | 'disconnect' | 'listTools' | 'callTool' | 'listSessions' | 'getSession' | 'finishAuth' | 'listPrompts' | 'getPrompt' | 'listResources' | 'readResource';
|
|
87
88
|
interface McpRpcRequest {
|
|
88
89
|
id: string;
|
|
@@ -126,7 +127,7 @@ interface ReadResourceParams {
|
|
|
126
127
|
uri: string;
|
|
127
128
|
}
|
|
128
129
|
interface FinishAuthParams {
|
|
129
|
-
|
|
130
|
+
state: string;
|
|
130
131
|
code: string;
|
|
131
132
|
}
|
|
132
133
|
type McpRpcParams = ConnectParams | DisconnectParams | SessionParams | CallToolParams | GetPromptParams | ReadResourceParams | FinishAuthParams | undefined;
|
|
@@ -137,11 +138,12 @@ interface SessionInfo {
|
|
|
137
138
|
serverUrl: string;
|
|
138
139
|
transport: TransportType;
|
|
139
140
|
createdAt: number;
|
|
141
|
+
updatedAt?: number;
|
|
140
142
|
/**
|
|
141
143
|
* Session readiness for auto-restore.
|
|
142
|
-
*
|
|
144
|
+
* `pending` means auth is in progress and should be resumed explicitly by user action.
|
|
143
145
|
*/
|
|
144
|
-
|
|
146
|
+
status: SessionStatus;
|
|
145
147
|
}
|
|
146
148
|
interface SessionListResult {
|
|
147
149
|
sessions: SessionInfo[];
|
|
@@ -12,17 +12,38 @@ CREATE TABLE IF NOT EXISTS public.mcp_sessions (
|
|
|
12
12
|
callback_url TEXT NOT NULL,
|
|
13
13
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
14
14
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
15
|
-
expires_at TIMESTAMPTZ
|
|
16
|
-
|
|
15
|
+
expires_at TIMESTAMPTZ,
|
|
16
|
+
status TEXT NOT NULL DEFAULT 'pending'
|
|
17
|
+
CHECK (status IN ('pending', 'active')),
|
|
17
18
|
headers JSONB,
|
|
19
|
+
auth_url TEXT,
|
|
20
|
+
CONSTRAINT mcp_sessions_user_session_unique
|
|
21
|
+
UNIQUE (user_id, session_id)
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
CREATE TABLE IF NOT EXISTS public.mcp_credentials (
|
|
25
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
26
|
+
session_id TEXT NOT NULL,
|
|
27
|
+
user_id TEXT NOT NULL,
|
|
18
28
|
client_information JSONB,
|
|
19
29
|
tokens JSONB,
|
|
20
30
|
code_verifier TEXT,
|
|
21
|
-
client_id TEXT
|
|
31
|
+
client_id TEXT,
|
|
32
|
+
oauth_state JSONB,
|
|
33
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
34
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
35
|
+
CONSTRAINT mcp_credentials_session_fk
|
|
36
|
+
FOREIGN KEY (user_id, session_id)
|
|
37
|
+
REFERENCES public.mcp_sessions(user_id, session_id)
|
|
38
|
+
ON DELETE CASCADE,
|
|
39
|
+
CONSTRAINT mcp_credentials_user_session_unique
|
|
40
|
+
UNIQUE (user_id, session_id)
|
|
22
41
|
);
|
|
23
42
|
|
|
24
43
|
CREATE INDEX IF NOT EXISTS idx_mcp_sessions_user_id ON public.mcp_sessions(user_id);
|
|
25
44
|
CREATE INDEX IF NOT EXISTS idx_mcp_sessions_expires_at ON public.mcp_sessions(expires_at);
|
|
45
|
+
CREATE INDEX IF NOT EXISTS idx_mcp_credentials_user_session
|
|
46
|
+
ON public.mcp_credentials(user_id, session_id);
|
|
26
47
|
|
|
27
48
|
CREATE OR REPLACE FUNCTION public.set_current_timestamp_updated_at()
|
|
28
49
|
RETURNS TRIGGER AS $$
|
|
@@ -39,6 +60,13 @@ BEFORE UPDATE ON public.mcp_sessions
|
|
|
39
60
|
FOR EACH ROW
|
|
40
61
|
EXECUTE FUNCTION public.set_current_timestamp_updated_at();
|
|
41
62
|
|
|
63
|
+
DROP TRIGGER IF EXISTS trg_mcp_credentials_updated_at ON public.mcp_credentials;
|
|
64
|
+
|
|
65
|
+
CREATE TRIGGER trg_mcp_credentials_updated_at
|
|
66
|
+
BEFORE UPDATE ON public.mcp_credentials
|
|
67
|
+
FOR EACH ROW
|
|
68
|
+
EXECUTE FUNCTION public.set_current_timestamp_updated_at();
|
|
69
|
+
|
|
42
70
|
-- Optional production configuration:
|
|
43
71
|
-- Create a dedicated app role and use its credentials in NEON_DATABASE_URL.
|
|
44
72
|
-- Replace neondb and the password before running.
|
|
@@ -47,6 +75,7 @@ EXECUTE FUNCTION public.set_current_timestamp_updated_at();
|
|
|
47
75
|
-- GRANT CONNECT ON DATABASE neondb TO mcp_service_role;
|
|
48
76
|
-- GRANT USAGE ON SCHEMA public TO mcp_service_role;
|
|
49
77
|
-- GRANT SELECT, INSERT, UPDATE, DELETE ON public.mcp_sessions TO mcp_service_role;
|
|
78
|
+
-- GRANT SELECT, INSERT, UPDATE, DELETE ON public.mcp_credentials TO mcp_service_role;
|
|
50
79
|
-- GRANT USAGE ON ALL SEQUENCES IN SCHEMA public TO mcp_service_role;
|
|
51
80
|
|
|
52
81
|
-- Optional RLS configuration:
|
|
@@ -60,6 +89,7 @@ EXECUTE FUNCTION public.set_current_timestamp_updated_at();
|
|
|
60
89
|
-- GRANT USAGE ON ALL SEQUENCES IN SCHEMA public TO mcp_service_role;
|
|
61
90
|
--
|
|
62
91
|
-- ALTER TABLE public.mcp_sessions ENABLE ROW LEVEL SECURITY;
|
|
92
|
+
-- ALTER TABLE public.mcp_credentials ENABLE ROW LEVEL SECURITY;
|
|
63
93
|
--
|
|
64
94
|
-- CREATE POLICY mcp_service_role_full_access
|
|
65
95
|
-- ON public.mcp_sessions
|
|
@@ -67,3 +97,10 @@ EXECUTE FUNCTION public.set_current_timestamp_updated_at();
|
|
|
67
97
|
-- TO mcp_service_role
|
|
68
98
|
-- USING (true)
|
|
69
99
|
-- WITH CHECK (true);
|
|
100
|
+
--
|
|
101
|
+
-- CREATE POLICY mcp_service_role_oauth_full_access
|
|
102
|
+
-- ON public.mcp_credentials
|
|
103
|
+
-- FOR ALL
|
|
104
|
+
-- TO mcp_service_role
|
|
105
|
+
-- USING (true)
|
|
106
|
+
-- WITH CHECK (true);
|
|
@@ -18,12 +18,12 @@ WHERE jobname IN (
|
|
|
18
18
|
);
|
|
19
19
|
|
|
20
20
|
-- Stage 1: Short-term Transient Purge (every 5 minutes)
|
|
21
|
-
-- Removes
|
|
22
|
-
-- sessions whose
|
|
21
|
+
-- Removes abandoned OAuth flows and other transient
|
|
22
|
+
-- sessions whose pending expiration has passed.
|
|
23
23
|
SELECT cron.schedule(
|
|
24
24
|
'mcp-cleanup-transient-sessions',
|
|
25
25
|
'*/5 * * * *',
|
|
26
|
-
$$DELETE FROM public.mcp_sessions WHERE expires_at < now() AND
|
|
26
|
+
$$DELETE FROM public.mcp_sessions WHERE expires_at IS NOT NULL AND expires_at < now() AND status <> 'active';$$
|
|
27
27
|
);
|
|
28
28
|
|
|
29
29
|
-- Stage 2: Long-term Dormancy Eviction (daily at midnight UTC)
|
|
@@ -31,5 +31,5 @@ SELECT cron.schedule(
|
|
|
31
31
|
SELECT cron.schedule(
|
|
32
32
|
'mcp-cleanup-dormant-sessions',
|
|
33
33
|
'0 0 * * *',
|
|
34
|
-
$$DELETE FROM public.mcp_sessions WHERE
|
|
34
|
+
$$DELETE FROM public.mcp_sessions WHERE status = 'active' AND updated_at < now() - interval '30 days';$$
|
|
35
35
|
);
|
|
@@ -10,19 +10,41 @@ CREATE TABLE IF NOT EXISTS public.mcp_sessions (
|
|
|
10
10
|
callback_url TEXT NOT NULL,
|
|
11
11
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
12
12
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
13
|
-
expires_at TIMESTAMPTZ
|
|
14
|
-
|
|
13
|
+
expires_at TIMESTAMPTZ,
|
|
14
|
+
status TEXT NOT NULL DEFAULT 'pending'
|
|
15
|
+
CHECK (status IN ('pending', 'active')),
|
|
15
16
|
headers JSONB,
|
|
17
|
+
auth_url TEXT,
|
|
18
|
+
CONSTRAINT mcp_sessions_user_session_unique
|
|
19
|
+
UNIQUE (user_id, session_id)
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
-- Runtime credentials are kept separate from connection metadata.
|
|
23
|
+
CREATE TABLE IF NOT EXISTS public.mcp_credentials (
|
|
24
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
25
|
+
session_id TEXT NOT NULL,
|
|
26
|
+
user_id TEXT NOT NULL,
|
|
16
27
|
client_information JSONB,
|
|
17
28
|
tokens JSONB,
|
|
18
29
|
code_verifier TEXT,
|
|
19
|
-
client_id TEXT
|
|
30
|
+
client_id TEXT,
|
|
31
|
+
oauth_state JSONB,
|
|
32
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
33
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
34
|
+
CONSTRAINT mcp_credentials_session_fk
|
|
35
|
+
FOREIGN KEY (user_id, session_id)
|
|
36
|
+
REFERENCES public.mcp_sessions(user_id, session_id)
|
|
37
|
+
ON DELETE CASCADE,
|
|
38
|
+
CONSTRAINT mcp_credentials_user_session_unique
|
|
39
|
+
UNIQUE (user_id, session_id)
|
|
20
40
|
);
|
|
21
41
|
|
|
22
42
|
-- Add an index on user_id for faster lookups
|
|
23
43
|
CREATE INDEX IF NOT EXISTS idx_mcp_sessions_user_id ON public.mcp_sessions(user_id);
|
|
24
44
|
-- Add an index on expires_at to speed up the cleanup job
|
|
25
45
|
CREATE INDEX IF NOT EXISTS idx_mcp_sessions_expires_at ON public.mcp_sessions(expires_at);
|
|
46
|
+
CREATE INDEX IF NOT EXISTS idx_mcp_credentials_user_session
|
|
47
|
+
ON public.mcp_credentials(user_id, session_id);
|
|
26
48
|
|
|
27
49
|
-- Trigger to automatically update the 'updated_at' column
|
|
28
50
|
CREATE OR REPLACE FUNCTION public.set_current_timestamp_updated_at()
|
|
@@ -39,8 +61,15 @@ BEFORE UPDATE ON public.mcp_sessions
|
|
|
39
61
|
FOR EACH ROW
|
|
40
62
|
EXECUTE FUNCTION public.set_current_timestamp_updated_at();
|
|
41
63
|
|
|
64
|
+
DROP TRIGGER IF EXISTS trg_mcp_credentials_updated_at ON public.mcp_credentials;
|
|
65
|
+
CREATE TRIGGER trg_mcp_credentials_updated_at
|
|
66
|
+
BEFORE UPDATE ON public.mcp_credentials
|
|
67
|
+
FOR EACH ROW
|
|
68
|
+
EXECUTE FUNCTION public.set_current_timestamp_updated_at();
|
|
69
|
+
|
|
42
70
|
-- Enable Row Level Security (RLS)
|
|
43
71
|
ALTER TABLE public.mcp_sessions ENABLE ROW LEVEL SECURITY;
|
|
72
|
+
ALTER TABLE public.mcp_credentials ENABLE ROW LEVEL SECURITY;
|
|
44
73
|
|
|
45
74
|
-- Policy 1: Users can read their own sessions
|
|
46
75
|
CREATE POLICY "Users can view their own sessions"
|
|
@@ -51,6 +80,14 @@ USING (
|
|
|
51
80
|
auth.uid()::text = user_id
|
|
52
81
|
);
|
|
53
82
|
|
|
83
|
+
CREATE POLICY "Users can view their own credentials"
|
|
84
|
+
ON public.mcp_credentials
|
|
85
|
+
FOR SELECT
|
|
86
|
+
TO authenticated
|
|
87
|
+
USING (
|
|
88
|
+
auth.uid()::text = user_id
|
|
89
|
+
);
|
|
90
|
+
|
|
54
91
|
-- Policy 2: Users can insert their own sessions
|
|
55
92
|
CREATE POLICY "Users can insert their own sessions"
|
|
56
93
|
ON public.mcp_sessions
|
|
@@ -60,6 +97,14 @@ WITH CHECK (
|
|
|
60
97
|
auth.uid()::text = user_id
|
|
61
98
|
);
|
|
62
99
|
|
|
100
|
+
CREATE POLICY "Users can insert their own credentials"
|
|
101
|
+
ON public.mcp_credentials
|
|
102
|
+
FOR INSERT
|
|
103
|
+
TO authenticated
|
|
104
|
+
WITH CHECK (
|
|
105
|
+
auth.uid()::text = user_id
|
|
106
|
+
);
|
|
107
|
+
|
|
63
108
|
-- Policy 3: Users can update their own sessions
|
|
64
109
|
CREATE POLICY "Users can update their own sessions"
|
|
65
110
|
ON public.mcp_sessions
|
|
@@ -72,6 +117,17 @@ WITH CHECK (
|
|
|
72
117
|
auth.uid()::text = user_id
|
|
73
118
|
);
|
|
74
119
|
|
|
120
|
+
CREATE POLICY "Users can update their own credentials"
|
|
121
|
+
ON public.mcp_credentials
|
|
122
|
+
FOR UPDATE
|
|
123
|
+
TO authenticated
|
|
124
|
+
USING (
|
|
125
|
+
auth.uid()::text = user_id
|
|
126
|
+
)
|
|
127
|
+
WITH CHECK (
|
|
128
|
+
auth.uid()::text = user_id
|
|
129
|
+
);
|
|
130
|
+
|
|
75
131
|
-- Policy 4: Users can delete their own sessions
|
|
76
132
|
CREATE POLICY "Users can delete their own sessions"
|
|
77
133
|
ON public.mcp_sessions
|
|
@@ -80,3 +136,11 @@ TO authenticated
|
|
|
80
136
|
USING (
|
|
81
137
|
auth.uid()::text = user_id
|
|
82
138
|
);
|
|
139
|
+
|
|
140
|
+
CREATE POLICY "Users can delete their own credentials"
|
|
141
|
+
ON public.mcp_credentials
|
|
142
|
+
FOR DELETE
|
|
143
|
+
TO authenticated
|
|
144
|
+
USING (
|
|
145
|
+
auth.uid()::text = user_id
|
|
146
|
+
);
|
|
@@ -5,27 +5,27 @@ CREATE EXTENSION IF NOT EXISTS pg_cron;
|
|
|
5
5
|
-- ─────────────────────────────────────────────────────────────────────────────
|
|
6
6
|
-- Stage 1: Short-term Transient Purge (every 5 minutes)
|
|
7
7
|
-- ─────────────────────────────────────────────────────────────────────────────
|
|
8
|
-
-- Targets sessions
|
|
9
|
-
--
|
|
8
|
+
-- Targets transient sessions (pending OAuth or setup states) whose
|
|
9
|
+
-- expiration has passed. Active sessions are explicitly
|
|
10
10
|
-- excluded from this sweep to preserve automation credentials.
|
|
11
11
|
--
|
|
12
12
|
-- The idx_mcp_sessions_expires_at index ensures this is a fast indexed scan.
|
|
13
13
|
SELECT cron.schedule(
|
|
14
14
|
'cleanup-transient-sessions',
|
|
15
15
|
'*/5 * * * *',
|
|
16
|
-
$$DELETE FROM public.mcp_sessions WHERE expires_at < now() AND
|
|
16
|
+
$$DELETE FROM public.mcp_sessions WHERE expires_at IS NOT NULL AND expires_at < now() AND status <> 'active';$$
|
|
17
17
|
);
|
|
18
18
|
|
|
19
19
|
-- ─────────────────────────────────────────────────────────────────────────────
|
|
20
20
|
-- Stage 2: Long-term Dormancy Eviction (daily at midnight UTC)
|
|
21
21
|
-- ─────────────────────────────────────────────────────────────────────────────
|
|
22
|
-
-- Safety net for sessions that were successfully established (
|
|
23
|
-
-- but have been completely untouched for 30+ days. This prevents
|
|
22
|
+
-- Safety net for sessions that were successfully established (status = 'active')
|
|
23
|
+
-- but have been completely untouched for 30+ days. This prevents active
|
|
24
24
|
-- records from persisting indefinitely if they are genuinely abandoned.
|
|
25
25
|
SELECT cron.schedule(
|
|
26
26
|
'cleanup-dormant-sessions',
|
|
27
27
|
'0 0 * * *',
|
|
28
|
-
$$DELETE FROM public.mcp_sessions WHERE
|
|
28
|
+
$$DELETE FROM public.mcp_sessions WHERE status = 'active' AND updated_at < now() - interval '30 days';$$
|
|
29
29
|
);
|
|
30
30
|
|
|
31
31
|
-- Add a comment on the extension for visibility in Supabase Dashboard
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mcp-ts/sdk",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.0",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
7
|
-
"description": "A lightweight MCP
|
|
7
|
+
"description": "A lightweight MCP client library for TypeScript with durable sessions across Redis, Supabase, Neon, and SQLite, cross-runtime agent support, and dynamic tool discovery to reduce LLM context usage.",
|
|
8
8
|
"main": "./dist/index.js",
|
|
9
9
|
"module": "./dist/index.mjs",
|
|
10
10
|
"types": "./dist/index.d.ts",
|
|
@@ -102,7 +102,17 @@
|
|
|
102
102
|
"react",
|
|
103
103
|
"hook",
|
|
104
104
|
"anthropic",
|
|
105
|
-
"claude"
|
|
105
|
+
"claude",
|
|
106
|
+
"typescript",
|
|
107
|
+
"agent",
|
|
108
|
+
"agents",
|
|
109
|
+
"durable-sessions",
|
|
110
|
+
"tool-discovery",
|
|
111
|
+
"tool-router",
|
|
112
|
+
"context-window",
|
|
113
|
+
"sqlite",
|
|
114
|
+
"supabase",
|
|
115
|
+
"neon"
|
|
106
116
|
],
|
|
107
117
|
"author": "MCP Assistant Contributors",
|
|
108
118
|
"license": "MIT",
|
|
@@ -117,8 +117,8 @@ export class SSEClient {
|
|
|
117
117
|
return this.sendRequest<GetSessionResult>('getSession', { sessionId });
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
-
async finishAuth(
|
|
121
|
-
return this.sendRequest<FinishAuthResult>('finishAuth', {
|
|
120
|
+
async finishAuth(state: string, code: string): Promise<FinishAuthResult> {
|
|
121
|
+
return this.sendRequest<FinishAuthResult>('finishAuth', { state, code });
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
async listPrompts(sessionId: string): Promise<ListPromptsResult> {
|
|
@@ -8,7 +8,7 @@ export { useMcp, type UseMcpOptions, type McpClient, type McpConnection } from '
|
|
|
8
8
|
|
|
9
9
|
// Optional OAuth popup conveniences. These are not required for auth:
|
|
10
10
|
// consumers can still provide their own onRedirect handler, callback page UI,
|
|
11
|
-
// or complete `finishAuth(
|
|
11
|
+
// or complete `finishAuth(state, code)` from a normal redirect flow.
|
|
12
12
|
export {
|
|
13
13
|
useMcpOAuthPopup,
|
|
14
14
|
openCenteredPopup,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useEffect, useRef, useState, type CSSProperties, type ReactNode } from 'react';
|
|
2
|
+
import { parseOAuthState } from '../../shared/utils.js';
|
|
2
3
|
|
|
3
4
|
export interface OAuthPopupConnectionLike {
|
|
4
5
|
sessionId: string;
|
|
@@ -11,7 +12,7 @@ export interface OAuthPopupConnectionLike {
|
|
|
11
12
|
*
|
|
12
13
|
* These utilities sit on top of the core MCP auth primitives:
|
|
13
14
|
* - `useMcp({ onRedirect })` to decide how auth navigation happens
|
|
14
|
-
* - `finishAuth(
|
|
15
|
+
* - `finishAuth(state, code)` to complete code exchange
|
|
15
16
|
*
|
|
16
17
|
* Consumers are free to:
|
|
17
18
|
* - use these helpers as-is for a turnkey popup flow
|
|
@@ -60,6 +61,7 @@ function postPopupResult(
|
|
|
60
61
|
popupWindow: WindowProxy | null,
|
|
61
62
|
result: {
|
|
62
63
|
sessionId?: string;
|
|
64
|
+
state?: string;
|
|
63
65
|
success: boolean;
|
|
64
66
|
error?: string;
|
|
65
67
|
}
|
|
@@ -150,9 +152,9 @@ export function createOAuthPopupRedirectHandler(
|
|
|
150
152
|
*/
|
|
151
153
|
export function useMcpOAuthPopup<TConnection extends OAuthPopupConnectionLike>(
|
|
152
154
|
connections: TConnection[],
|
|
153
|
-
finishAuth: (
|
|
155
|
+
finishAuth: (state: string, code: string) => Promise<unknown>
|
|
154
156
|
): void {
|
|
155
|
-
const pendingPopupsRef = useRef<Map<string, WindowProxy>>(new Map());
|
|
157
|
+
const pendingPopupsRef = useRef<Map<string, { popupWindow: WindowProxy | null; state: string }>>(new Map());
|
|
156
158
|
const processingCodesRef = useRef<Set<string>>(new Set());
|
|
157
159
|
|
|
158
160
|
useEffect(() => {
|
|
@@ -169,10 +171,16 @@ export function useMcpOAuthPopup<TConnection extends OAuthPopupConnectionLike>(
|
|
|
169
171
|
const popupWindow = event.source && 'postMessage' in event.source
|
|
170
172
|
? event.source as WindowProxy
|
|
171
173
|
: null;
|
|
172
|
-
const
|
|
174
|
+
const rawState =
|
|
175
|
+
typeof event.data.state === 'string'
|
|
176
|
+
? event.data.state
|
|
177
|
+
: typeof event.data.sessionId === 'string'
|
|
178
|
+
? event.data.sessionId
|
|
179
|
+
: '';
|
|
180
|
+
const targetSessionId = rawState ? parseOAuthState(rawState)?.sessionId || rawState : '';
|
|
173
181
|
|
|
174
182
|
if (popupWindow && targetSessionId) {
|
|
175
|
-
pendingPopupsRef.current.set(targetSessionId, popupWindow);
|
|
183
|
+
pendingPopupsRef.current.set(targetSessionId, { popupWindow, state: rawState });
|
|
176
184
|
}
|
|
177
185
|
|
|
178
186
|
if (!targetSessionId) {
|
|
@@ -190,6 +198,7 @@ export function useMcpOAuthPopup<TConnection extends OAuthPopupConnectionLike>(
|
|
|
190
198
|
if (popupWindow) {
|
|
191
199
|
postPopupResult(popupWindow, {
|
|
192
200
|
sessionId: targetSessionId,
|
|
201
|
+
state: rawState,
|
|
193
202
|
success: false,
|
|
194
203
|
error: 'OAuth session not found in the current client state',
|
|
195
204
|
});
|
|
@@ -204,13 +213,14 @@ export function useMcpOAuthPopup<TConnection extends OAuthPopupConnectionLike>(
|
|
|
204
213
|
processingCodesRef.current.add(codeKey);
|
|
205
214
|
|
|
206
215
|
try {
|
|
207
|
-
await finishAuth(
|
|
216
|
+
await finishAuth(rawState, code);
|
|
208
217
|
} catch (error) {
|
|
209
218
|
processingCodesRef.current.delete(codeKey);
|
|
210
219
|
pendingPopupsRef.current.delete(targetSession.sessionId);
|
|
211
220
|
if (popupWindow) {
|
|
212
221
|
postPopupResult(popupWindow, {
|
|
213
|
-
sessionId:
|
|
222
|
+
sessionId: rawState,
|
|
223
|
+
state: rawState,
|
|
214
224
|
success: false,
|
|
215
225
|
error: error instanceof Error ? error.message : 'Failed to finish auth',
|
|
216
226
|
});
|
|
@@ -237,11 +247,14 @@ export function useMcpOAuthPopup<TConnection extends OAuthPopupConnectionLike>(
|
|
|
237
247
|
|
|
238
248
|
useEffect(() => {
|
|
239
249
|
for (const connection of connections) {
|
|
240
|
-
const
|
|
250
|
+
const pendingPopup = pendingPopupsRef.current.get(connection.sessionId);
|
|
251
|
+
const popupWindow = pendingPopup?.popupWindow || null;
|
|
252
|
+
const resultState = pendingPopup?.state || connection.sessionId;
|
|
241
253
|
|
|
242
254
|
if (connection.state === 'AUTHENTICATED' || connection.state === 'READY' || connection.state === 'CONNECTED') {
|
|
243
255
|
postPopupResult(popupWindow, {
|
|
244
|
-
sessionId:
|
|
256
|
+
sessionId: resultState,
|
|
257
|
+
state: resultState,
|
|
245
258
|
success: true,
|
|
246
259
|
});
|
|
247
260
|
for (const codeKey of processingCodesRef.current) {
|
|
@@ -255,7 +268,8 @@ export function useMcpOAuthPopup<TConnection extends OAuthPopupConnectionLike>(
|
|
|
255
268
|
|
|
256
269
|
if (connection.state === 'FAILED') {
|
|
257
270
|
postPopupResult(popupWindow, {
|
|
258
|
-
sessionId:
|
|
271
|
+
sessionId: resultState,
|
|
272
|
+
state: resultState,
|
|
259
273
|
success: false,
|
|
260
274
|
error: connection.error || 'Failed to complete authorization',
|
|
261
275
|
});
|
|
@@ -277,7 +291,7 @@ export function useMcpOAuthPopup<TConnection extends OAuthPopupConnectionLike>(
|
|
|
277
291
|
* opener window, waits for success/failure, and closes the popup on success.
|
|
278
292
|
*
|
|
279
293
|
* It is intentionally optional: apps can replace it with their own callback
|
|
280
|
-
* page UI or skip popup auth entirely and call `finishAuth(
|
|
294
|
+
* page UI or skip popup auth entirely and call `finishAuth(state, code)`
|
|
281
295
|
* from any callback route they control.
|
|
282
296
|
*/
|
|
283
297
|
export function McpOAuthCallbackContent({
|
|
@@ -328,7 +342,14 @@ export function McpOAuthCallbackContent({
|
|
|
328
342
|
return;
|
|
329
343
|
}
|
|
330
344
|
|
|
331
|
-
|
|
345
|
+
const resultState =
|
|
346
|
+
typeof event.data.state === 'string'
|
|
347
|
+
? event.data.state
|
|
348
|
+
: typeof event.data.sessionId === 'string'
|
|
349
|
+
? event.data.sessionId
|
|
350
|
+
: '';
|
|
351
|
+
|
|
352
|
+
if (resultState !== sessionId) {
|
|
332
353
|
return;
|
|
333
354
|
}
|
|
334
355
|
|
|
@@ -352,7 +373,7 @@ export function McpOAuthCallbackContent({
|
|
|
352
373
|
window.addEventListener('message', handleResult);
|
|
353
374
|
channel?.addEventListener('message', handleResult);
|
|
354
375
|
|
|
355
|
-
const payload = { type: AUTH_CODE_MESSAGE, code, sessionId };
|
|
376
|
+
const payload = { type: AUTH_CODE_MESSAGE, code, state: sessionId, sessionId };
|
|
356
377
|
|
|
357
378
|
if (window.opener) {
|
|
358
379
|
try {
|