@studiometa/productive-mcp 0.10.7 → 0.10.9
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/dist/errors.d.ts.map +1 -1
- package/dist/formatters.d.ts +4 -0
- package/dist/formatters.d.ts.map +1 -1
- package/dist/handlers/activities.d.ts +98 -3
- package/dist/handlers/activities.d.ts.map +1 -1
- package/dist/handlers/attachments.d.ts +98 -3
- package/dist/handlers/attachments.d.ts.map +1 -1
- package/dist/handlers/bookings.d.ts +98 -3
- package/dist/handlers/bookings.d.ts.map +1 -1
- package/dist/handlers/comments.d.ts +98 -3
- package/dist/handlers/comments.d.ts.map +1 -1
- package/dist/handlers/companies.d.ts +98 -3
- package/dist/handlers/companies.d.ts.map +1 -1
- package/dist/handlers/custom-fields.d.ts +121 -0
- package/dist/handlers/custom-fields.d.ts.map +1 -0
- package/dist/handlers/deals.d.ts +98 -3
- package/dist/handlers/deals.d.ts.map +1 -1
- package/dist/handlers/discussions.d.ts +98 -3
- package/dist/handlers/discussions.d.ts.map +1 -1
- package/dist/handlers/help.d.ts.map +1 -1
- package/dist/handlers/index.d.ts.map +1 -1
- package/dist/handlers/pages.d.ts +98 -3
- package/dist/handlers/pages.d.ts.map +1 -1
- package/dist/handlers/projects.d.ts +98 -3
- package/dist/handlers/projects.d.ts.map +1 -1
- package/dist/handlers/schema.d.ts.map +1 -1
- package/dist/handlers/services.d.ts +98 -3
- package/dist/handlers/services.d.ts.map +1 -1
- package/dist/handlers/tasks.d.ts +98 -3
- package/dist/handlers/tasks.d.ts.map +1 -1
- package/dist/handlers/time.d.ts +98 -3
- package/dist/handlers/time.d.ts.map +1 -1
- package/dist/handlers/timers.d.ts +98 -3
- package/dist/handlers/timers.d.ts.map +1 -1
- package/dist/handlers/valid-includes.d.ts.map +1 -1
- package/dist/{handlers-CzOijI7B.js → handlers-t95fhdps.js} +159 -3
- package/dist/handlers-t95fhdps.js.map +1 -0
- package/dist/handlers.js +1 -1
- package/dist/hints.d.ts +4 -0
- package/dist/hints.d.ts.map +1 -1
- package/dist/http.d.ts +7 -7
- package/dist/http.d.ts.map +1 -1
- package/dist/http.js +52 -42
- package/dist/http.js.map +1 -1
- package/dist/index.js +3 -3
- package/dist/oauth.d.ts +9 -9
- package/dist/oauth.d.ts.map +1 -1
- package/dist/oauth.js +39 -39
- package/dist/oauth.js.map +1 -1
- package/dist/schema.d.ts +64 -62
- package/dist/schema.d.ts.map +1 -1
- package/dist/server.js +4 -4
- package/dist/server.js.map +1 -1
- package/dist/{handlers-Cha6_ulB.js → stdio-Bi1Lvp8O.js} +97 -2
- package/dist/stdio-Bi1Lvp8O.js.map +1 -0
- package/dist/stdio.d.ts +4 -4
- package/dist/stdio.js +2 -99
- package/dist/{version-zLvx5_bN.js → version-BFw4junA.js} +3 -3
- package/dist/{version-zLvx5_bN.js.map → version-BFw4junA.js.map} +1 -1
- package/package.json +10 -18
- package/skills/SKILL.md +36 -18
- package/dist/handlers-Cha6_ulB.js.map +0 -1
- package/dist/handlers-CzOijI7B.js.map +0 -1
- package/dist/stdio.js.map +0 -1
package/dist/hints.d.ts
CHANGED
|
@@ -74,6 +74,10 @@ export declare function getPageHints(pageId: string): ContextualHints;
|
|
|
74
74
|
* Generate hints for a discussion
|
|
75
75
|
*/
|
|
76
76
|
export declare function getDiscussionHints(discussionId: string, pageId?: string): ContextualHints;
|
|
77
|
+
/**
|
|
78
|
+
* Generate hints for a custom field definition
|
|
79
|
+
*/
|
|
80
|
+
export declare function getCustomFieldHints(fieldId: string): ContextualHints;
|
|
77
81
|
/**
|
|
78
82
|
* Generate hints for a timer
|
|
79
83
|
*/
|
package/dist/hints.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hints.d.ts","sourceRoot":"","sources":["../src/hints.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,eAAe;IAC9B,iBAAiB,CAAC,EAAE,YAAY,EAAE,CAAC;IACnC,cAAc,CAAC,EAAE,UAAU,EAAE,CAAC;CAC/B;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,eAAe,
|
|
1
|
+
{"version":3,"file":"hints.d.ts","sourceRoot":"","sources":["../src/hints.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,eAAe;IAC9B,iBAAiB,CAAC,EAAE,YAAY,EAAE,CAAC;IACnC,cAAc,CAAC,EAAE,UAAU,EAAE,CAAC;CAC/B;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,eAAe,CAsEhF;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,eAAe,CA8DlE;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,eAAe,CA6D5D;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,eAAe,CAyChE;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACrC,eAAe,GAAG,IAAI,CAkBxB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,eAAe,CAqDlE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,eAAe,CAmDlE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,MAAM,EACnB,MAAM,CAAC,EAAE,MAAM,EACf,SAAS,CAAC,EAAE,MAAM,GACjB,eAAe,CA0CjB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC7B,UAAU,EAAE,MAAM,EAClB,eAAe,CAAC,EAAE,MAAM,EACxB,aAAa,CAAC,EAAE,MAAM,GACrB,eAAe,CA4BjB;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,aAAa,EAAE,MAAM,EACrB,cAAc,CAAC,EAAE,MAAM,GACtB,eAAe,CAqCjB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,eAAe,CA6BrF;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,eAAe,CAqD5D;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,YAAY,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,eAAe,CA+CzF;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe,CAyBpE;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,eAAe,CA4BlF"}
|
package/dist/http.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* This module contains the app/router creation logic for the HTTP transport.
|
|
5
5
|
* The actual server startup is in server.ts.
|
|
6
6
|
*/
|
|
7
|
-
import {
|
|
7
|
+
import { H3 } from 'h3';
|
|
8
8
|
/**
|
|
9
9
|
* JSON-RPC error response
|
|
10
10
|
*/
|
|
@@ -44,6 +44,7 @@ export declare function handleInitialize(): {
|
|
|
44
44
|
*/
|
|
45
45
|
export declare function handleToolsList(): {
|
|
46
46
|
tools: {
|
|
47
|
+
description?: string | undefined;
|
|
47
48
|
inputSchema: {
|
|
48
49
|
[x: string]: unknown;
|
|
49
50
|
type: "object";
|
|
@@ -52,8 +53,6 @@ export declare function handleToolsList(): {
|
|
|
52
53
|
} | undefined;
|
|
53
54
|
required?: string[] | undefined;
|
|
54
55
|
};
|
|
55
|
-
name: string;
|
|
56
|
-
description?: string | undefined;
|
|
57
56
|
outputSchema?: {
|
|
58
57
|
[x: string]: unknown;
|
|
59
58
|
type: "object";
|
|
@@ -70,7 +69,7 @@ export declare function handleToolsList(): {
|
|
|
70
69
|
openWorldHint?: boolean | undefined;
|
|
71
70
|
} | undefined;
|
|
72
71
|
execution?: {
|
|
73
|
-
taskSupport?: "
|
|
72
|
+
taskSupport?: "forbidden" | "optional" | "required" | undefined;
|
|
74
73
|
} | undefined;
|
|
75
74
|
_meta?: {
|
|
76
75
|
[x: string]: unknown;
|
|
@@ -79,13 +78,14 @@ export declare function handleToolsList(): {
|
|
|
79
78
|
src: string;
|
|
80
79
|
mimeType?: string | undefined;
|
|
81
80
|
sizes?: string[] | undefined;
|
|
82
|
-
theme?: "
|
|
81
|
+
theme?: "dark" | "light" | undefined;
|
|
83
82
|
}[] | undefined;
|
|
83
|
+
name: string;
|
|
84
84
|
title?: string | undefined;
|
|
85
85
|
}[];
|
|
86
86
|
};
|
|
87
87
|
/**
|
|
88
|
-
* Create the
|
|
88
|
+
* Create the H3 application with all routes
|
|
89
89
|
*/
|
|
90
|
-
export declare function createHttpApp():
|
|
90
|
+
export declare function createHttpApp(): H3;
|
|
91
91
|
//# sourceMappingURL=http.d.ts.map
|
package/dist/http.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,EAAE,EAA+B,MAAM,IAAI,CAAC;AAgBrD;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,GAAE,MAAM,GAAG,MAAM,GAAG,IAAW;;;;;;;EAM5F;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE,GAAE,MAAM,GAAG,MAAM,GAAG,IAAW;;;;EAMhF;AAED;;GAEG;AACH,wBAAgB,gBAAgB;;;;;;;;;;;EAa/B;AAED;;GAEG;AACH,wBAAgB,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAE9B;AAWD;;GAEG;AACH,wBAAgB,aAAa,IAAI,EAAE,CA0LlC"}
|
package/dist/http.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { a as INSTRUCTIONS, i as readResource, n as listResourceTemplates, r as listResources, t as VERSION } from "./version-
|
|
2
|
-
import { t as executeToolWithCredentials } from "./handlers-
|
|
3
|
-
import "./handlers.js";
|
|
1
|
+
import { a as INSTRUCTIONS, i as readResource, n as listResourceTemplates, r as listResources, t as VERSION } from "./version-BFw4junA.js";
|
|
2
|
+
import { t as executeToolWithCredentials } from "./handlers-t95fhdps.js";
|
|
4
3
|
import { TOOLS } from "./tools.js";
|
|
5
4
|
import { parseAuthHeader } from "./auth.js";
|
|
6
5
|
import { authorizeGetHandler, authorizePostHandler, oauthMetadataHandler, registerHandler, tokenHandler } from "./oauth.js";
|
|
7
|
-
import {
|
|
6
|
+
import { H3, defineHandler } from "h3";
|
|
8
7
|
/**
|
|
9
8
|
* HTTP transport handlers for Productive MCP Server
|
|
10
9
|
*
|
|
@@ -58,21 +57,26 @@ function handleToolsList() {
|
|
|
58
57
|
return { tools: TOOLS };
|
|
59
58
|
}
|
|
60
59
|
/**
|
|
61
|
-
*
|
|
60
|
+
* Get base URL from event headers
|
|
61
|
+
*/
|
|
62
|
+
function getBaseUrl(event) {
|
|
63
|
+
const host = event.req.headers.get("host") || "localhost:3000";
|
|
64
|
+
return `${event.req.headers.get("x-forwarded-proto") || "http"}://${host}`;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Create the H3 application with all routes
|
|
62
68
|
*/
|
|
63
69
|
function createHttpApp() {
|
|
64
|
-
const app =
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
setResponseHeader(event, "Content-Type", "application/json");
|
|
75
|
-
setResponseHeader(event, "Cache-Control", "public, max-age=3600");
|
|
70
|
+
const app = new H3();
|
|
71
|
+
app.get("/.well-known/oauth-authorization-server", oauthMetadataHandler);
|
|
72
|
+
app.post("/register", registerHandler);
|
|
73
|
+
app.get("/authorize", authorizeGetHandler);
|
|
74
|
+
app.post("/authorize", authorizePostHandler);
|
|
75
|
+
app.post("/token", tokenHandler);
|
|
76
|
+
app.get("/.well-known/oauth-protected-resource", defineHandler((event) => {
|
|
77
|
+
const baseUrl = getBaseUrl(event);
|
|
78
|
+
event.res.headers.set("Content-Type", "application/json");
|
|
79
|
+
event.res.headers.set("Cache-Control", "public, max-age=3600");
|
|
76
80
|
return {
|
|
77
81
|
resource: `${baseUrl}/mcp`,
|
|
78
82
|
authorization_servers: [baseUrl],
|
|
@@ -80,36 +84,35 @@ function createHttpApp() {
|
|
|
80
84
|
bearer_methods_supported: ["header"]
|
|
81
85
|
};
|
|
82
86
|
}));
|
|
83
|
-
|
|
87
|
+
app.get("/", defineHandler(() => {
|
|
84
88
|
return {
|
|
85
89
|
status: "ok",
|
|
86
90
|
service: "productive-mcp",
|
|
87
91
|
version: VERSION
|
|
88
92
|
};
|
|
89
93
|
}));
|
|
90
|
-
|
|
94
|
+
app.get("/health", defineHandler(() => {
|
|
91
95
|
return { status: "ok" };
|
|
92
96
|
}));
|
|
93
|
-
|
|
94
|
-
const credentials = parseAuthHeader(
|
|
97
|
+
app.post("/mcp", defineHandler(async (event) => {
|
|
98
|
+
const credentials = parseAuthHeader(event.req.headers.get("authorization"));
|
|
95
99
|
if (!credentials) {
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
event.node.res.statusCode = 401;
|
|
100
|
+
const baseUrl = getBaseUrl(event);
|
|
101
|
+
event.res.headers.set("Content-Type", "application/json");
|
|
102
|
+
event.res.headers.set("WWW-Authenticate", `Bearer resource_metadata="${baseUrl}/.well-known/oauth-protected-resource"`);
|
|
103
|
+
event.res.status = 401;
|
|
101
104
|
return jsonRpcError(-32001, "Authentication required. Provide Bearer token with base64(organizationId:apiToken:userId)");
|
|
102
105
|
}
|
|
103
|
-
|
|
106
|
+
event.res.headers.set("Content-Type", "application/json");
|
|
104
107
|
let body;
|
|
105
108
|
try {
|
|
106
|
-
body = await
|
|
109
|
+
body = await event.req.json();
|
|
107
110
|
} catch {
|
|
108
|
-
event.
|
|
111
|
+
event.res.status = 400;
|
|
109
112
|
return jsonRpcError(-32700, "Parse error: Invalid JSON");
|
|
110
113
|
}
|
|
111
114
|
if (!body || typeof body !== "object") {
|
|
112
|
-
event.
|
|
115
|
+
event.res.status = 400;
|
|
113
116
|
return jsonRpcError(-32700, "Parse error: Invalid JSON");
|
|
114
117
|
}
|
|
115
118
|
const { method, params, id } = body;
|
|
@@ -132,27 +135,34 @@ function createHttpApp() {
|
|
|
132
135
|
return jsonRpcError(-32603, `Internal error: ${error instanceof Error ? error.message : String(error)}`, id ?? null);
|
|
133
136
|
}
|
|
134
137
|
}));
|
|
135
|
-
|
|
136
|
-
if (!parseAuthHeader(
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
event.
|
|
138
|
+
app.get("/mcp/sse", defineHandler(async (event) => {
|
|
139
|
+
if (!parseAuthHeader(event.req.headers.get("authorization"))) {
|
|
140
|
+
const baseUrl = getBaseUrl(event);
|
|
141
|
+
event.res.headers.set("WWW-Authenticate", `Bearer resource_metadata="${baseUrl}/.well-known/oauth-protected-resource"`);
|
|
142
|
+
event.res.status = 401;
|
|
140
143
|
return { error: "Authentication required" };
|
|
141
144
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
+
const nodeRuntime = event.runtime?.node;
|
|
146
|
+
const nodeRes = nodeRuntime?.res;
|
|
147
|
+
if (!nodeRes) {
|
|
148
|
+
event.res.status = 501;
|
|
149
|
+
return { error: "SSE requires Node.js runtime" };
|
|
150
|
+
}
|
|
151
|
+
nodeRes.writeHead(200, {
|
|
152
|
+
"Content-Type": "text/event-stream",
|
|
153
|
+
"Cache-Control": "no-cache",
|
|
154
|
+
Connection: "keep-alive"
|
|
155
|
+
});
|
|
145
156
|
const sessionId = crypto.randomUUID();
|
|
146
|
-
|
|
157
|
+
nodeRes.write(`event: session\ndata: ${JSON.stringify({ sessionId })}\n\n`);
|
|
147
158
|
const keepAlive = setInterval(() => {
|
|
148
|
-
|
|
159
|
+
nodeRes.write(": keepalive\n\n");
|
|
149
160
|
}, 3e4);
|
|
150
|
-
|
|
161
|
+
nodeRuntime?.req.on("close", () => {
|
|
151
162
|
clearInterval(keepAlive);
|
|
152
163
|
});
|
|
153
164
|
return new Promise(() => {});
|
|
154
165
|
}));
|
|
155
|
-
app.use(router);
|
|
156
166
|
return app;
|
|
157
167
|
}
|
|
158
168
|
export { createHttpApp, handleInitialize, handleToolsList, jsonRpcError, jsonRpcSuccess };
|
package/dist/http.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.js","names":[],"sources":["../src/http.ts"],"sourcesContent":["/**\n * HTTP transport handlers for Productive MCP Server\n *\n * This module contains the app/router creation logic for the HTTP transport.\n * The actual server startup is in server.ts.\n */\n\nimport {\n createApp,\n createRouter,\n defineEventHandler,\n readBody,\n getHeader,\n setResponseHeader,\n type App,\n} from 'h3';\n\nimport { parseAuthHeader } from './auth.js';\nimport { executeToolWithCredentials } from './handlers.js';\nimport { INSTRUCTIONS } from './instructions.js';\nimport {\n oauthMetadataHandler,\n registerHandler,\n authorizeGetHandler,\n authorizePostHandler,\n tokenHandler,\n} from './oauth.js';\nimport { listResources, listResourceTemplates, readResource } from './resources.js';\nimport { TOOLS } from './tools.js';\nimport { VERSION } from './version.js';\n\n/**\n * JSON-RPC error response\n */\nexport function jsonRpcError(code: number, message: string, id: string | number | null = null) {\n return {\n jsonrpc: '2.0',\n error: { code, message },\n id,\n };\n}\n\n/**\n * JSON-RPC success response\n */\nexport function jsonRpcSuccess(result: unknown, id: string | number | null = null) {\n return {\n jsonrpc: '2.0',\n result,\n id,\n };\n}\n\n/**\n * Handle the initialize JSON-RPC method\n */\nexport function handleInitialize() {\n return {\n protocolVersion: '2024-11-05',\n serverInfo: {\n name: 'productive-mcp',\n version: VERSION,\n },\n capabilities: {\n tools: {},\n resources: {},\n },\n instructions: INSTRUCTIONS,\n };\n}\n\n/**\n * Handle the tools/list JSON-RPC method\n */\nexport function handleToolsList() {\n return { tools: TOOLS };\n}\n\n/**\n * Create the h3 application with all routes\n */\nexport function createHttpApp(): App {\n const app = createApp();\n const router = createRouter();\n\n // OAuth 2.0 endpoints for Claude Desktop integration (MCP auth spec)\n router.get('/.well-known/oauth-authorization-server', oauthMetadataHandler);\n router.post('/register', registerHandler); // Dynamic Client Registration (RFC 7591)\n router.get('/authorize', authorizeGetHandler);\n router.post('/authorize', authorizePostHandler);\n router.post('/token', tokenHandler);\n\n // OAuth Protected Resource Metadata (RFC 9728 / MCP spec 2025-03-26)\n // This endpoint tells clients where to find the authorization server\n router.get(\n '/.well-known/oauth-protected-resource',\n defineEventHandler((event) => {\n const host = event.node.req.headers.host || 'localhost:3000';\n const protocol = event.node.req.headers['x-forwarded-proto'] || 'http';\n const baseUrl = `${protocol}://${host}`;\n\n setResponseHeader(event, 'Content-Type', 'application/json');\n setResponseHeader(event, 'Cache-Control', 'public, max-age=3600');\n\n return {\n resource: `${baseUrl}/mcp`,\n authorization_servers: [baseUrl],\n scopes_supported: ['productive'],\n bearer_methods_supported: ['header'],\n };\n }),\n );\n\n // Health check endpoint\n router.get(\n '/',\n defineEventHandler(() => {\n return { status: 'ok', service: 'productive-mcp', version: VERSION };\n }),\n );\n\n router.get(\n '/health',\n defineEventHandler(() => {\n return { status: 'ok' };\n }),\n );\n\n // MCP endpoint - handles JSON-RPC over HTTP\n router.post(\n '/mcp',\n defineEventHandler(async (event) => {\n // Parse authorization header\n const authHeader = getHeader(event, 'authorization');\n const credentials = parseAuthHeader(authHeader);\n\n if (!credentials) {\n // RFC 6750: Return WWW-Authenticate header to trigger OAuth flow\n const host = event.node.req.headers.host || 'localhost:3000';\n const protocol = event.node.req.headers['x-forwarded-proto'] || 'http';\n const baseUrl = `${protocol}://${host}`;\n\n setResponseHeader(event, 'Content-Type', 'application/json');\n setResponseHeader(\n event,\n 'WWW-Authenticate',\n `Bearer resource_metadata=\"${baseUrl}/.well-known/oauth-protected-resource\"`,\n );\n event.node.res.statusCode = 401;\n return jsonRpcError(\n -32001,\n 'Authentication required. Provide Bearer token with base64(organizationId:apiToken:userId)',\n );\n }\n\n setResponseHeader(event, 'Content-Type', 'application/json');\n\n // Parse JSON-RPC request\n let body: { method?: string; params?: unknown; id?: string | number };\n try {\n body = await readBody(event);\n } catch {\n event.node.res.statusCode = 400;\n return jsonRpcError(-32700, 'Parse error: Invalid JSON');\n }\n\n if (!body || typeof body !== 'object') {\n event.node.res.statusCode = 400;\n return jsonRpcError(-32700, 'Parse error: Invalid JSON');\n }\n\n const { method, params, id } = body;\n\n try {\n if (method === 'initialize') {\n return jsonRpcSuccess(handleInitialize(), id ?? null);\n }\n\n if (method === 'tools/list') {\n return jsonRpcSuccess(handleToolsList(), id ?? null);\n }\n\n if (method === 'tools/call') {\n const { name, arguments: args } = params as {\n name: string;\n arguments?: Record<string, unknown>;\n };\n const result = await executeToolWithCredentials(name, args || {}, credentials);\n return jsonRpcSuccess(result, id ?? null);\n }\n\n if (method === 'resources/list') {\n return jsonRpcSuccess({ resources: listResources() }, id ?? null);\n }\n\n if (method === 'resources/templates/list') {\n return jsonRpcSuccess({ resourceTemplates: listResourceTemplates() }, id ?? null);\n }\n\n if (method === 'resources/read') {\n const { uri } = (params as { uri: string }) ?? {};\n if (!uri) {\n return jsonRpcError(-32602, 'Invalid params: uri is required', id ?? null);\n }\n const result = await readResource(uri, credentials);\n return jsonRpcSuccess(result, id ?? null);\n }\n\n // Unknown method\n return jsonRpcError(-32601, `Method not found: ${method}`, id ?? null);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return jsonRpcError(-32603, `Internal error: ${message}`, id ?? null);\n }\n }),\n );\n\n // SSE endpoint for server-sent events (optional, for streaming responses)\n router.get(\n '/mcp/sse',\n defineEventHandler(async (event) => {\n const authHeader = getHeader(event, 'authorization');\n const credentials = parseAuthHeader(authHeader);\n\n if (!credentials) {\n // RFC 6750: Return WWW-Authenticate header to trigger OAuth flow\n const host = event.node.req.headers.host || 'localhost:3000';\n const protocol = event.node.req.headers['x-forwarded-proto'] || 'http';\n const baseUrl = `${protocol}://${host}`;\n\n setResponseHeader(\n event,\n 'WWW-Authenticate',\n `Bearer resource_metadata=\"${baseUrl}/.well-known/oauth-protected-resource\"`,\n );\n event.node.res.statusCode = 401;\n return { error: 'Authentication required' };\n }\n\n // Set SSE headers\n setResponseHeader(event, 'Content-Type', 'text/event-stream');\n setResponseHeader(event, 'Cache-Control', 'no-cache');\n setResponseHeader(event, 'Connection', 'keep-alive');\n\n // Generate session ID and send it\n const sessionId = crypto.randomUUID();\n\n // Send initial session event\n event.node.res.write(`event: session\\ndata: ${JSON.stringify({ sessionId })}\\n\\n`);\n\n // Keep connection alive\n const keepAlive = setInterval(() => {\n event.node.res.write(': keepalive\\n\\n');\n }, 30000);\n\n // Clean up on close\n event.node.req.on('close', () => {\n clearInterval(keepAlive);\n });\n\n // Don't end the response - keep it open for SSE\n return new Promise(() => {});\n }),\n );\n\n app.use(router);\n return app;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"http.js","names":[],"sources":["../src/http.ts"],"sourcesContent":["/**\n * HTTP transport handlers for Productive MCP Server\n *\n * This module contains the app/router creation logic for the HTTP transport.\n * The actual server startup is in server.ts.\n */\n\nimport { H3, defineHandler, type H3Event } from 'h3';\n\nimport { parseAuthHeader } from './auth.js';\nimport { executeToolWithCredentials } from './handlers.js';\nimport { INSTRUCTIONS } from './instructions.js';\nimport {\n oauthMetadataHandler,\n registerHandler,\n authorizeGetHandler,\n authorizePostHandler,\n tokenHandler,\n} from './oauth.js';\nimport { listResources, listResourceTemplates, readResource } from './resources.js';\nimport { TOOLS } from './tools.js';\nimport { VERSION } from './version.js';\n\n/**\n * JSON-RPC error response\n */\nexport function jsonRpcError(code: number, message: string, id: string | number | null = null) {\n return {\n jsonrpc: '2.0',\n error: { code, message },\n id,\n };\n}\n\n/**\n * JSON-RPC success response\n */\nexport function jsonRpcSuccess(result: unknown, id: string | number | null = null) {\n return {\n jsonrpc: '2.0',\n result,\n id,\n };\n}\n\n/**\n * Handle the initialize JSON-RPC method\n */\nexport function handleInitialize() {\n return {\n protocolVersion: '2024-11-05',\n serverInfo: {\n name: 'productive-mcp',\n version: VERSION,\n },\n capabilities: {\n tools: {},\n resources: {},\n },\n instructions: INSTRUCTIONS,\n };\n}\n\n/**\n * Handle the tools/list JSON-RPC method\n */\nexport function handleToolsList() {\n return { tools: TOOLS };\n}\n\n/**\n * Get base URL from event headers\n */\nfunction getBaseUrl(event: H3Event): string {\n const host = event.req.headers.get('host') || 'localhost:3000';\n const protocol = event.req.headers.get('x-forwarded-proto') || 'http';\n return `${protocol}://${host}`;\n}\n\n/**\n * Create the H3 application with all routes\n */\nexport function createHttpApp(): H3 {\n const app = new H3();\n\n // OAuth 2.0 endpoints for Claude Desktop integration (MCP auth spec)\n app.get('/.well-known/oauth-authorization-server', oauthMetadataHandler);\n app.post('/register', registerHandler); // Dynamic Client Registration (RFC 7591)\n app.get('/authorize', authorizeGetHandler);\n app.post('/authorize', authorizePostHandler);\n app.post('/token', tokenHandler);\n\n // OAuth Protected Resource Metadata (RFC 9728 / MCP spec 2025-03-26)\n // This endpoint tells clients where to find the authorization server\n app.get(\n '/.well-known/oauth-protected-resource',\n defineHandler((event) => {\n const baseUrl = getBaseUrl(event);\n\n event.res.headers.set('Content-Type', 'application/json');\n event.res.headers.set('Cache-Control', 'public, max-age=3600');\n\n return {\n resource: `${baseUrl}/mcp`,\n authorization_servers: [baseUrl],\n scopes_supported: ['productive'],\n bearer_methods_supported: ['header'],\n };\n }),\n );\n\n // Health check endpoint\n app.get(\n '/',\n defineHandler(() => {\n return { status: 'ok', service: 'productive-mcp', version: VERSION };\n }),\n );\n\n app.get(\n '/health',\n defineHandler(() => {\n return { status: 'ok' };\n }),\n );\n\n // MCP endpoint - handles JSON-RPC over HTTP\n app.post(\n '/mcp',\n defineHandler(async (event) => {\n // Parse authorization header\n const authHeader = event.req.headers.get('authorization');\n const credentials = parseAuthHeader(authHeader);\n\n if (!credentials) {\n // RFC 6750: Return WWW-Authenticate header to trigger OAuth flow\n const baseUrl = getBaseUrl(event);\n\n event.res.headers.set('Content-Type', 'application/json');\n event.res.headers.set(\n 'WWW-Authenticate',\n `Bearer resource_metadata=\"${baseUrl}/.well-known/oauth-protected-resource\"`,\n );\n event.res.status = 401;\n return jsonRpcError(\n -32001,\n 'Authentication required. Provide Bearer token with base64(organizationId:apiToken:userId)',\n );\n }\n\n event.res.headers.set('Content-Type', 'application/json');\n\n // Parse JSON-RPC request\n let body: { method?: string; params?: unknown; id?: string | number } | undefined;\n try {\n body = await event.req.json();\n } catch {\n event.res.status = 400;\n return jsonRpcError(-32700, 'Parse error: Invalid JSON');\n }\n\n if (!body || typeof body !== 'object') {\n event.res.status = 400;\n return jsonRpcError(-32700, 'Parse error: Invalid JSON');\n }\n\n const { method, params, id } = body;\n\n try {\n if (method === 'initialize') {\n return jsonRpcSuccess(handleInitialize(), id ?? null);\n }\n\n if (method === 'tools/list') {\n return jsonRpcSuccess(handleToolsList(), id ?? null);\n }\n\n if (method === 'tools/call') {\n const { name, arguments: args } = params as {\n name: string;\n arguments?: Record<string, unknown>;\n };\n const result = await executeToolWithCredentials(name, args || {}, credentials);\n return jsonRpcSuccess(result, id ?? null);\n }\n\n if (method === 'resources/list') {\n return jsonRpcSuccess({ resources: listResources() }, id ?? null);\n }\n\n if (method === 'resources/templates/list') {\n return jsonRpcSuccess({ resourceTemplates: listResourceTemplates() }, id ?? null);\n }\n\n if (method === 'resources/read') {\n const { uri } = (params as { uri: string }) ?? {};\n if (!uri) {\n return jsonRpcError(-32602, 'Invalid params: uri is required', id ?? null);\n }\n const result = await readResource(uri, credentials);\n return jsonRpcSuccess(result, id ?? null);\n }\n\n // Unknown method\n return jsonRpcError(-32601, `Method not found: ${method}`, id ?? null);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return jsonRpcError(-32603, `Internal error: ${message}`, id ?? null);\n }\n }),\n );\n\n // SSE endpoint for server-sent events (optional, for streaming responses)\n app.get(\n '/mcp/sse',\n defineHandler(async (event) => {\n const authHeader = event.req.headers.get('authorization');\n const credentials = parseAuthHeader(authHeader);\n\n if (!credentials) {\n // RFC 6750: Return WWW-Authenticate header to trigger OAuth flow\n const baseUrl = getBaseUrl(event);\n\n event.res.headers.set(\n 'WWW-Authenticate',\n `Bearer resource_metadata=\"${baseUrl}/.well-known/oauth-protected-resource\"`,\n );\n event.res.status = 401;\n return { error: 'Authentication required' };\n }\n\n // Node.js-specific SSE: access raw res for streaming\n const nodeRuntime = event.runtime?.node;\n const nodeRes = nodeRuntime?.res as import('node:http').ServerResponse | undefined;\n if (!nodeRes) {\n event.res.status = 501;\n return { error: 'SSE requires Node.js runtime' };\n }\n\n // Write SSE headers directly to Node.js response (bypassing h3's pipeline)\n nodeRes.writeHead(200, {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n });\n\n // Generate session ID and send it\n const sessionId = crypto.randomUUID();\n\n // Send initial session event\n nodeRes.write(`event: session\\ndata: ${JSON.stringify({ sessionId })}\\n\\n`);\n\n // Keep connection alive\n const keepAlive = setInterval(() => {\n nodeRes.write(': keepalive\\n\\n');\n }, 30000);\n\n // Clean up on close\n nodeRuntime?.req.on('close', () => {\n clearInterval(keepAlive);\n });\n\n // Don't end the response - keep it open for SSE\n return new Promise(() => {});\n }),\n );\n\n return app;\n}\n"],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as INSTRUCTIONS, i as readResource, n as listResourceTemplates, r as listResources, t as VERSION } from "./version-
|
|
3
|
-
import "./handlers-
|
|
4
|
-
import {
|
|
2
|
+
import { a as INSTRUCTIONS, i as readResource, n as listResourceTemplates, r as listResources, t as VERSION } from "./version-BFw4junA.js";
|
|
3
|
+
import "./handlers-t95fhdps.js";
|
|
4
|
+
import { a as handlePrompt, n as getAvailableTools, s as handleToolCall, t as getAvailablePrompts } from "./stdio-Bi1Lvp8O.js";
|
|
5
5
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
6
6
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
7
7
|
import { CallToolRequestSchema, GetPromptRequestSchema, ListPromptsRequestSchema, ListResourceTemplatesRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
package/dist/oauth.d.ts
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
*
|
|
21
21
|
* MCP clients MUST check this endpoint first for server capabilities.
|
|
22
22
|
*/
|
|
23
|
-
export declare const oauthMetadataHandler: import("h3").
|
|
23
|
+
export declare const oauthMetadataHandler: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, {
|
|
24
24
|
issuer: string;
|
|
25
25
|
authorization_endpoint: string;
|
|
26
26
|
token_endpoint: string;
|
|
@@ -40,7 +40,7 @@ export declare const oauthMetadataHandler: import("h3").EventHandler<import("h3"
|
|
|
40
40
|
* Since we use stateless tokens, we accept any registration and return
|
|
41
41
|
* a generated client_id.
|
|
42
42
|
*/
|
|
43
|
-
export declare const registerHandler: import("h3").
|
|
43
|
+
export declare const registerHandler: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
44
44
|
error: string;
|
|
45
45
|
error_description: string;
|
|
46
46
|
client_id?: undefined;
|
|
@@ -50,25 +50,25 @@ export declare const registerHandler: import("h3").EventHandler<import("h3").Eve
|
|
|
50
50
|
grant_types?: undefined;
|
|
51
51
|
response_types?: undefined;
|
|
52
52
|
} | {
|
|
53
|
+
error?: undefined;
|
|
54
|
+
error_description?: undefined;
|
|
53
55
|
client_id: string;
|
|
54
56
|
client_name: string;
|
|
55
57
|
redirect_uris: string[];
|
|
56
58
|
token_endpoint_auth_method: string;
|
|
57
59
|
grant_types: string[];
|
|
58
60
|
response_types: string[];
|
|
59
|
-
error?: undefined;
|
|
60
|
-
error_description?: undefined;
|
|
61
61
|
}>>;
|
|
62
62
|
/**
|
|
63
63
|
* Authorization endpoint - shows login form
|
|
64
64
|
* GET /authorize
|
|
65
65
|
*/
|
|
66
|
-
export declare const authorizeGetHandler: import("h3").
|
|
66
|
+
export declare const authorizeGetHandler: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, string | import("h3").HTTPResponse>;
|
|
67
67
|
/**
|
|
68
68
|
* Authorization endpoint - process login
|
|
69
69
|
* POST /authorize
|
|
70
70
|
*/
|
|
71
|
-
export declare const authorizePostHandler: import("h3").
|
|
71
|
+
export declare const authorizePostHandler: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<string>>;
|
|
72
72
|
/**
|
|
73
73
|
* Token endpoint - exchange code for access token
|
|
74
74
|
* POST /token
|
|
@@ -77,7 +77,7 @@ export declare const authorizePostHandler: import("h3").EventHandler<import("h3"
|
|
|
77
77
|
* - authorization_code grant (with PKCE validation)
|
|
78
78
|
* - refresh_token grant
|
|
79
79
|
*/
|
|
80
|
-
export declare const tokenHandler: import("h3").
|
|
80
|
+
export declare const tokenHandler: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
81
81
|
error: string;
|
|
82
82
|
error_description: string;
|
|
83
83
|
access_token?: undefined;
|
|
@@ -85,11 +85,11 @@ export declare const tokenHandler: import("h3").EventHandler<import("h3").EventH
|
|
|
85
85
|
expires_in?: undefined;
|
|
86
86
|
refresh_token?: undefined;
|
|
87
87
|
} | {
|
|
88
|
+
error?: undefined;
|
|
89
|
+
error_description?: undefined;
|
|
88
90
|
access_token: string;
|
|
89
91
|
token_type: string;
|
|
90
92
|
expires_in: number;
|
|
91
93
|
refresh_token: string;
|
|
92
|
-
error?: undefined;
|
|
93
|
-
error_description?: undefined;
|
|
94
94
|
}>>;
|
|
95
95
|
//# sourceMappingURL=oauth.d.ts.map
|
package/dist/oauth.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oauth.d.ts","sourceRoot":"","sources":["../src/oauth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;
|
|
1
|
+
{"version":3,"file":"oauth.d.ts","sourceRoot":"","sources":["../src/oauth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAQH;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;EAyB/B,CAAC;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;GAoC1B,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,mBAAmB,0GA+C9B,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,oBAAoB,uFA0D/B,CAAC;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY;;;;;;;;;;;;;;GA2FvB,CAAC"}
|
package/dist/oauth.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createAuthToken } from "./auth.js";
|
|
2
2
|
import { createAuthCode, decodeAuthCode } from "./crypto.js";
|
|
3
|
-
import {
|
|
3
|
+
import { defineHandler, getQuery, redirect } from "h3";
|
|
4
4
|
import { createHash } from "node:crypto";
|
|
5
5
|
/**
|
|
6
6
|
* OAuth 2.0 endpoints for Claude Desktop integration
|
|
@@ -24,11 +24,11 @@ import { createHash } from "node:crypto";
|
|
|
24
24
|
*
|
|
25
25
|
* MCP clients MUST check this endpoint first for server capabilities.
|
|
26
26
|
*/
|
|
27
|
-
const oauthMetadataHandler =
|
|
28
|
-
const host = event.
|
|
29
|
-
const baseUrl = `${event.
|
|
30
|
-
|
|
31
|
-
|
|
27
|
+
const oauthMetadataHandler = defineHandler((event) => {
|
|
28
|
+
const host = event.req.headers.get("host") || "localhost:3000";
|
|
29
|
+
const baseUrl = `${event.req.headers.get("x-forwarded-proto") || "http"}://${host}`;
|
|
30
|
+
event.res.headers.set("Content-Type", "application/json");
|
|
31
|
+
event.res.headers.set("Cache-Control", "public, max-age=3600");
|
|
32
32
|
return {
|
|
33
33
|
issuer: baseUrl,
|
|
34
34
|
authorization_endpoint: `${baseUrl}/authorize`,
|
|
@@ -50,13 +50,13 @@ const oauthMetadataHandler = defineEventHandler((event) => {
|
|
|
50
50
|
* Since we use stateless tokens, we accept any registration and return
|
|
51
51
|
* a generated client_id.
|
|
52
52
|
*/
|
|
53
|
-
const registerHandler =
|
|
54
|
-
|
|
53
|
+
const registerHandler = defineHandler(async (event) => {
|
|
54
|
+
event.res.headers.set("Content-Type", "application/json");
|
|
55
55
|
let body;
|
|
56
56
|
try {
|
|
57
|
-
body = await
|
|
57
|
+
body = await event.req.json();
|
|
58
58
|
} catch {
|
|
59
|
-
event.
|
|
59
|
+
event.res.status = 400;
|
|
60
60
|
return {
|
|
61
61
|
error: "invalid_request",
|
|
62
62
|
error_description: "Invalid JSON body"
|
|
@@ -68,7 +68,7 @@ const registerHandler = defineEventHandler(async (event) => {
|
|
|
68
68
|
name: clientName,
|
|
69
69
|
ts: Date.now()
|
|
70
70
|
})).toString("base64url");
|
|
71
|
-
event.
|
|
71
|
+
event.res.status = 201;
|
|
72
72
|
return {
|
|
73
73
|
client_id: clientId,
|
|
74
74
|
client_name: clientName,
|
|
@@ -82,7 +82,7 @@ const registerHandler = defineEventHandler(async (event) => {
|
|
|
82
82
|
* Authorization endpoint - shows login form
|
|
83
83
|
* GET /authorize
|
|
84
84
|
*/
|
|
85
|
-
const authorizeGetHandler =
|
|
85
|
+
const authorizeGetHandler = defineHandler((event) => {
|
|
86
86
|
const query = getQuery(event);
|
|
87
87
|
const clientId = query.client_id;
|
|
88
88
|
const redirectUri = query.redirect_uri;
|
|
@@ -91,8 +91,8 @@ const authorizeGetHandler = defineEventHandler((event) => {
|
|
|
91
91
|
const codeChallengeMethod = query.code_challenge_method;
|
|
92
92
|
const scope = query.scope;
|
|
93
93
|
if (!redirectUri) {
|
|
94
|
-
|
|
95
|
-
event.
|
|
94
|
+
event.res.headers.set("Content-Type", "text/html; charset=utf-8");
|
|
95
|
+
event.res.status = 400;
|
|
96
96
|
return renderErrorPage("Missing required parameter: redirect_uri");
|
|
97
97
|
}
|
|
98
98
|
if (!codeChallenge) {
|
|
@@ -100,16 +100,16 @@ const authorizeGetHandler = defineEventHandler((event) => {
|
|
|
100
100
|
errorUrl.searchParams.set("error", "invalid_request");
|
|
101
101
|
errorUrl.searchParams.set("error_description", "code_challenge is required");
|
|
102
102
|
if (state) errorUrl.searchParams.set("state", state);
|
|
103
|
-
return
|
|
103
|
+
return redirect(errorUrl.toString());
|
|
104
104
|
}
|
|
105
105
|
if (codeChallengeMethod && codeChallengeMethod !== "S256") {
|
|
106
106
|
const errorUrl = new URL(redirectUri);
|
|
107
107
|
errorUrl.searchParams.set("error", "invalid_request");
|
|
108
108
|
errorUrl.searchParams.set("error_description", "Only S256 code_challenge_method is supported");
|
|
109
109
|
if (state) errorUrl.searchParams.set("state", state);
|
|
110
|
-
return
|
|
110
|
+
return redirect(errorUrl.toString());
|
|
111
111
|
}
|
|
112
|
-
|
|
112
|
+
event.res.headers.set("Content-Type", "text/html; charset=utf-8");
|
|
113
113
|
return renderLoginForm({
|
|
114
114
|
clientId,
|
|
115
115
|
redirectUri,
|
|
@@ -123,11 +123,12 @@ const authorizeGetHandler = defineEventHandler((event) => {
|
|
|
123
123
|
* Authorization endpoint - process login
|
|
124
124
|
* POST /authorize
|
|
125
125
|
*/
|
|
126
|
-
const authorizePostHandler =
|
|
127
|
-
const
|
|
126
|
+
const authorizePostHandler = defineHandler(async (event) => {
|
|
127
|
+
const formData = await event.req.formData();
|
|
128
|
+
const { orgId, apiToken, userId, redirectUri, state, codeChallenge, codeChallengeMethod } = Object.fromEntries(formData.entries());
|
|
128
129
|
if (!redirectUri) {
|
|
129
|
-
|
|
130
|
-
event.
|
|
130
|
+
event.res.headers.set("Content-Type", "text/html; charset=utf-8");
|
|
131
|
+
event.res.status = 400;
|
|
131
132
|
return renderErrorPage("Missing redirect_uri parameter");
|
|
132
133
|
}
|
|
133
134
|
try {
|
|
@@ -135,15 +136,15 @@ const authorizePostHandler = defineEventHandler(async (event) => {
|
|
|
135
136
|
const isLocalhost = uri.hostname === "localhost" || uri.hostname === "127.0.0.1";
|
|
136
137
|
const isHttps = uri.protocol === "https:";
|
|
137
138
|
if (!isLocalhost && !isHttps) {
|
|
138
|
-
event.
|
|
139
|
+
event.res.status = 400;
|
|
139
140
|
return renderErrorPage("redirect_uri must be HTTPS or localhost");
|
|
140
141
|
}
|
|
141
142
|
} catch {
|
|
142
|
-
event.
|
|
143
|
+
event.res.status = 400;
|
|
143
144
|
return renderErrorPage("Invalid redirect_uri format");
|
|
144
145
|
}
|
|
145
146
|
if (!orgId || !apiToken) {
|
|
146
|
-
|
|
147
|
+
event.res.headers.set("Content-Type", "text/html; charset=utf-8");
|
|
147
148
|
return renderLoginForm({
|
|
148
149
|
redirectUri,
|
|
149
150
|
state,
|
|
@@ -162,7 +163,7 @@ const authorizePostHandler = defineEventHandler(async (event) => {
|
|
|
162
163
|
const redirectUrl = new URL(redirectUri);
|
|
163
164
|
redirectUrl.searchParams.set("code", code);
|
|
164
165
|
if (state) redirectUrl.searchParams.set("state", state);
|
|
165
|
-
|
|
166
|
+
event.res.headers.set("Content-Type", "text/html; charset=utf-8");
|
|
166
167
|
return renderSuccessPage(redirectUrl.toString());
|
|
167
168
|
});
|
|
168
169
|
/**
|
|
@@ -173,32 +174,31 @@ const authorizePostHandler = defineEventHandler(async (event) => {
|
|
|
173
174
|
* - authorization_code grant (with PKCE validation)
|
|
174
175
|
* - refresh_token grant
|
|
175
176
|
*/
|
|
176
|
-
const tokenHandler =
|
|
177
|
-
|
|
177
|
+
const tokenHandler = defineHandler(async (event) => {
|
|
178
|
+
event.res.headers.set("Content-Type", "application/json");
|
|
178
179
|
let body;
|
|
179
|
-
if ((event.
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
} else body = await readBody(event);
|
|
180
|
+
if ((event.req.headers.get("content-type") || "").includes("application/x-www-form-urlencoded")) {
|
|
181
|
+
const rawText = await event.req.text();
|
|
182
|
+
body = Object.fromEntries(new URLSearchParams(rawText));
|
|
183
|
+
} else body = await event.req.json();
|
|
184
184
|
const { grant_type, code, code_verifier, refresh_token } = body;
|
|
185
185
|
if (grant_type === "refresh_token") return handleRefreshToken(event, refresh_token);
|
|
186
186
|
if (grant_type !== "authorization_code") {
|
|
187
|
-
event.
|
|
187
|
+
event.res.status = 400;
|
|
188
188
|
return {
|
|
189
189
|
error: "unsupported_grant_type",
|
|
190
190
|
error_description: "Supported grant types: authorization_code, refresh_token"
|
|
191
191
|
};
|
|
192
192
|
}
|
|
193
193
|
if (!code) {
|
|
194
|
-
event.
|
|
194
|
+
event.res.status = 400;
|
|
195
195
|
return {
|
|
196
196
|
error: "invalid_request",
|
|
197
197
|
error_description: "Missing authorization code"
|
|
198
198
|
};
|
|
199
199
|
}
|
|
200
200
|
if (!code_verifier) {
|
|
201
|
-
event.
|
|
201
|
+
event.res.status = 400;
|
|
202
202
|
return {
|
|
203
203
|
error: "invalid_request",
|
|
204
204
|
error_description: "Missing code_verifier (PKCE required)"
|
|
@@ -208,7 +208,7 @@ const tokenHandler = defineEventHandler(async (event) => {
|
|
|
208
208
|
const payload = decodeAuthCode(code);
|
|
209
209
|
if (payload.codeChallenge) {
|
|
210
210
|
if (createS256Challenge(code_verifier) !== payload.codeChallenge) {
|
|
211
|
-
event.
|
|
211
|
+
event.res.status = 400;
|
|
212
212
|
return {
|
|
213
213
|
error: "invalid_grant",
|
|
214
214
|
error_description: "Invalid code_verifier"
|
|
@@ -230,7 +230,7 @@ const tokenHandler = defineEventHandler(async (event) => {
|
|
|
230
230
|
}, 86400 * 30)
|
|
231
231
|
};
|
|
232
232
|
} catch (error) {
|
|
233
|
-
event.
|
|
233
|
+
event.res.status = 400;
|
|
234
234
|
return {
|
|
235
235
|
error: "invalid_grant",
|
|
236
236
|
error_description: error instanceof Error ? error.message : "Invalid authorization code"
|
|
@@ -242,7 +242,7 @@ const tokenHandler = defineEventHandler(async (event) => {
|
|
|
242
242
|
*/
|
|
243
243
|
function handleRefreshToken(event, refreshToken) {
|
|
244
244
|
if (!refreshToken) {
|
|
245
|
-
event.
|
|
245
|
+
event.res.status = 400;
|
|
246
246
|
return {
|
|
247
247
|
error: "invalid_request",
|
|
248
248
|
error_description: "Missing refresh_token"
|
|
@@ -265,7 +265,7 @@ function handleRefreshToken(event, refreshToken) {
|
|
|
265
265
|
}, 86400 * 30)
|
|
266
266
|
};
|
|
267
267
|
} catch (error) {
|
|
268
|
-
event.
|
|
268
|
+
event.res.status = 400;
|
|
269
269
|
return {
|
|
270
270
|
error: "invalid_grant",
|
|
271
271
|
error_description: error instanceof Error ? error.message : "Invalid refresh token"
|