u
This commit is contained in:
23
src/index.js
23
src/index.js
@@ -230,6 +230,16 @@ import articlelistGetItems from './store/articlelist/getItemsByArticleSku.js';
|
||||
import articlelistSetItems from './store/articlelist/setItemsByArticleSku.js';
|
||||
import optionslistGetOptions from './store/optionslist/getOptionsByArticleSku.js';
|
||||
import optionslistSetOptions from './store/optionslist/setOptionsByArticleSku.js';
|
||||
import mcpRegisterClient from './store/mcp/registerClient.js';
|
||||
import mcpGetClient from './store/mcp/getClient.js';
|
||||
import mcpListClients from './store/mcp/listClients.js';
|
||||
import mcpDeleteClient from './store/mcp/deleteClient.js';
|
||||
import mcpCreateAuthCode from './store/mcp/createAuthCode.js';
|
||||
import mcpGetAuthCode from './store/mcp/getAuthCode.js';
|
||||
import mcpConsumeAuthCode from './store/mcp/consumeAuthCode.js';
|
||||
import mcpCreateRefreshToken from './store/mcp/createRefreshToken.js';
|
||||
import mcpGetRefreshToken from './store/mcp/getRefreshToken.js';
|
||||
import mcpRevokeRefreshToken from './store/mcp/revokeRefreshToken.js';
|
||||
|
||||
export default class LibDashStore {
|
||||
/**
|
||||
@@ -619,6 +629,19 @@ export default class LibDashStore {
|
||||
setOptionsByArticleSku: optionslistSetOptions(provider),
|
||||
};
|
||||
|
||||
this.mcp = {
|
||||
registerClient: mcpRegisterClient(provider),
|
||||
getClient: mcpGetClient(provider),
|
||||
listClients: mcpListClients(provider),
|
||||
deleteClient: mcpDeleteClient(provider),
|
||||
createAuthCode: mcpCreateAuthCode(provider),
|
||||
getAuthCode: mcpGetAuthCode(provider),
|
||||
consumeAuthCode: mcpConsumeAuthCode(provider),
|
||||
createRefreshToken: mcpCreateRefreshToken(provider),
|
||||
getRefreshToken: mcpGetRefreshToken(provider),
|
||||
revokeRefreshToken: mcpRevokeRefreshToken(provider),
|
||||
};
|
||||
|
||||
this.purge = storePurge(provider);
|
||||
this.initialize = storeInitialize(provider);
|
||||
|
||||
|
||||
90
src/providers/mssql/queries/mcp.js
Normal file
90
src/providers/mssql/queries/mcp.js
Normal file
@@ -0,0 +1,90 @@
|
||||
export const INSERT_CLIENT = `
|
||||
INSERT INTO mcp_client (client_id, client_secret, client_name, redirect_uris, grant_types, response_types, scope)
|
||||
OUTPUT INSERTED.id, INSERTED.client_id, INSERTED.client_secret, INSERTED.client_name, INSERTED.redirect_uris, INSERTED.grant_types, INSERTED.response_types, INSERTED.scope, INSERTED.created_at
|
||||
VALUES (@clientId, @clientSecret, @clientName, @redirectUris, @grantTypes, @responseTypes, @scope)
|
||||
`;
|
||||
|
||||
export const GET_CLIENT = `
|
||||
SELECT
|
||||
id,
|
||||
client_id AS clientId,
|
||||
client_secret AS clientSecret,
|
||||
client_name AS clientName,
|
||||
redirect_uris AS redirectUris,
|
||||
grant_types AS grantTypes,
|
||||
response_types AS responseTypes,
|
||||
scope,
|
||||
created_at AS createdAt
|
||||
FROM mcp_client
|
||||
WHERE client_id = @clientId
|
||||
`;
|
||||
|
||||
export const LIST_CLIENTS = `
|
||||
SELECT
|
||||
id,
|
||||
client_id AS clientId,
|
||||
client_name AS clientName,
|
||||
redirect_uris AS redirectUris,
|
||||
grant_types AS grantTypes,
|
||||
response_types AS responseTypes,
|
||||
scope,
|
||||
created_at AS createdAt
|
||||
FROM mcp_client
|
||||
ORDER BY created_at DESC
|
||||
`;
|
||||
|
||||
export const DELETE_CLIENT = `
|
||||
DELETE FROM mcp_client
|
||||
WHERE client_id = @clientId
|
||||
`;
|
||||
|
||||
export const INSERT_AUTH_CODE = `
|
||||
INSERT INTO mcp_auth_code (code, client_id, google_auth_id, redirect_uri, code_challenge, code_challenge_method, scope, expires_at)
|
||||
VALUES (@code, @clientId, @googleAuthId, @redirectUri, @codeChallenge, @codeChallengeMethod, @scope, @expiresAt)
|
||||
`;
|
||||
|
||||
export const GET_AUTH_CODE = `
|
||||
SELECT
|
||||
code,
|
||||
client_id AS clientId,
|
||||
google_auth_id AS googleAuthId,
|
||||
redirect_uri AS redirectUri,
|
||||
code_challenge AS codeChallenge,
|
||||
code_challenge_method AS codeChallengeMethod,
|
||||
scope,
|
||||
expires_at AS expiresAt,
|
||||
used
|
||||
FROM mcp_auth_code
|
||||
WHERE code = @code
|
||||
`;
|
||||
|
||||
export const CONSUME_AUTH_CODE = `
|
||||
UPDATE mcp_auth_code
|
||||
SET used = 1
|
||||
WHERE code = @code
|
||||
`;
|
||||
|
||||
export const INSERT_REFRESH_TOKEN = `
|
||||
INSERT INTO mcp_token (refresh_token, client_id, google_auth_id, scope, expires_at)
|
||||
OUTPUT INSERTED.id
|
||||
VALUES (@refreshToken, @clientId, @googleAuthId, @scope, @expiresAt)
|
||||
`;
|
||||
|
||||
export const GET_REFRESH_TOKEN = `
|
||||
SELECT
|
||||
id,
|
||||
refresh_token AS refreshToken,
|
||||
client_id AS clientId,
|
||||
google_auth_id AS googleAuthId,
|
||||
scope,
|
||||
expires_at AS expiresAt,
|
||||
revoked
|
||||
FROM mcp_token
|
||||
WHERE refresh_token = @refreshToken
|
||||
`;
|
||||
|
||||
export const REVOKE_REFRESH_TOKEN = `
|
||||
UPDATE mcp_token
|
||||
SET revoked = 1
|
||||
WHERE refresh_token = @refreshToken
|
||||
`;
|
||||
@@ -1177,6 +1177,61 @@ BEGIN
|
||||
END
|
||||
`;
|
||||
|
||||
export const INIT_MCP_CLIENT = `
|
||||
IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'mcp_client')
|
||||
BEGIN
|
||||
CREATE TABLE mcp_client (
|
||||
id INT IDENTITY(1,1) PRIMARY KEY,
|
||||
client_id NVARCHAR(100) NOT NULL,
|
||||
client_secret NVARCHAR(255) NULL,
|
||||
client_name NVARCHAR(255) NOT NULL,
|
||||
redirect_uris NVARCHAR(MAX) NOT NULL,
|
||||
grant_types NVARCHAR(255) NULL,
|
||||
response_types NVARCHAR(255) NULL,
|
||||
scope NVARCHAR(255) NULL,
|
||||
created_at DATETIME2 NOT NULL CONSTRAINT DF_mcp_client_created_at DEFAULT SYSUTCDATETIME(),
|
||||
CONSTRAINT UQ_mcp_client_client_id UNIQUE (client_id)
|
||||
);
|
||||
END
|
||||
`;
|
||||
|
||||
export const INIT_MCP_AUTH_CODE = `
|
||||
IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'mcp_auth_code')
|
||||
BEGIN
|
||||
CREATE TABLE mcp_auth_code (
|
||||
code NVARCHAR(128) PRIMARY KEY,
|
||||
client_id NVARCHAR(100) NOT NULL,
|
||||
google_auth_id INT NOT NULL,
|
||||
redirect_uri NVARCHAR(2048) NOT NULL,
|
||||
code_challenge NVARCHAR(255) NOT NULL,
|
||||
code_challenge_method NVARCHAR(32) NOT NULL,
|
||||
scope NVARCHAR(255) NULL,
|
||||
expires_at DATETIME2 NOT NULL,
|
||||
used TINYINT NOT NULL CONSTRAINT DF_mcp_auth_code_used DEFAULT 0,
|
||||
created_at DATETIME2 NOT NULL CONSTRAINT DF_mcp_auth_code_created_at DEFAULT SYSUTCDATETIME(),
|
||||
CONSTRAINT FK_mcp_auth_code_google_auth FOREIGN KEY (google_auth_id) REFERENCES google_auth(id) ON DELETE CASCADE
|
||||
);
|
||||
END
|
||||
`;
|
||||
|
||||
export const INIT_MCP_TOKEN = `
|
||||
IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'mcp_token')
|
||||
BEGIN
|
||||
CREATE TABLE mcp_token (
|
||||
id INT IDENTITY(1,1) PRIMARY KEY,
|
||||
refresh_token NVARCHAR(128) NOT NULL,
|
||||
client_id NVARCHAR(100) NOT NULL,
|
||||
google_auth_id INT NOT NULL,
|
||||
scope NVARCHAR(255) NULL,
|
||||
expires_at DATETIME2 NOT NULL,
|
||||
revoked TINYINT NOT NULL CONSTRAINT DF_mcp_token_revoked DEFAULT 0,
|
||||
created_at DATETIME2 NOT NULL CONSTRAINT DF_mcp_token_created_at DEFAULT SYSUTCDATETIME(),
|
||||
CONSTRAINT UQ_mcp_token_refresh UNIQUE (refresh_token),
|
||||
CONSTRAINT FK_mcp_token_google_auth FOREIGN KEY (google_auth_id) REFERENCES google_auth(id) ON DELETE CASCADE
|
||||
);
|
||||
END
|
||||
`;
|
||||
|
||||
/** DDL scripts in dependency order for schema init. */
|
||||
export const SCHEMA_SCRIPTS = [
|
||||
INIT_META,
|
||||
@@ -1244,4 +1299,7 @@ export const SCHEMA_SCRIPTS = [
|
||||
INIT_PAYMENT,
|
||||
INIT_SHIPPING,
|
||||
INIT_PACKAGE,
|
||||
INIT_MCP_CLIENT,
|
||||
INIT_MCP_AUTH_CODE,
|
||||
INIT_MCP_TOKEN,
|
||||
];
|
||||
|
||||
@@ -19,13 +19,23 @@ function normalizeParamValue(value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
function bindParams(params) {
|
||||
return Object.fromEntries(
|
||||
function bindParams(params, sql = null) {
|
||||
const bound = Object.fromEntries(
|
||||
Object.entries(params).map(([key, value]) => [
|
||||
key.replace(/^[@:$]/, ''),
|
||||
normalizeParamValue(value),
|
||||
]),
|
||||
);
|
||||
if (typeof sql === 'string') {
|
||||
const matches = sql.matchAll(/[@:$]([a-zA-Z0-9_]+)/g);
|
||||
for (const match of matches) {
|
||||
const name = match[1];
|
||||
if (!(name in bound)) {
|
||||
bound[name] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
return bound;
|
||||
}
|
||||
|
||||
function normalizeInsertId(value) {
|
||||
@@ -227,7 +237,7 @@ export class BetterSqlite3Executor {
|
||||
/** @returns {Promise<import('../provider.js').QueryResult>} */
|
||||
async query(sql, params = {}) {
|
||||
const statement = this.db.prepare(sql);
|
||||
const bindings = bindParams(params);
|
||||
const bindings = bindParams(params, sql);
|
||||
|
||||
if (statement.reader) {
|
||||
const rows = statement.all(bindings);
|
||||
|
||||
16
src/providers/sqlite3/queries/mcp.js
Normal file
16
src/providers/sqlite3/queries/mcp.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import * as source from '../../mssql/queries/mcp.js';
|
||||
import { translateQueries } from './translate.js';
|
||||
|
||||
const queries = translateQueries(source, 'mcp');
|
||||
export const {
|
||||
INSERT_CLIENT,
|
||||
GET_CLIENT,
|
||||
LIST_CLIENTS,
|
||||
DELETE_CLIENT,
|
||||
INSERT_AUTH_CODE,
|
||||
GET_AUTH_CODE,
|
||||
CONSUME_AUTH_CODE,
|
||||
INSERT_REFRESH_TOKEN,
|
||||
GET_REFRESH_TOKEN,
|
||||
REVOKE_REFRESH_TOKEN,
|
||||
} = queries;
|
||||
@@ -845,6 +845,50 @@ CREATE TABLE IF NOT EXISTS package (
|
||||
);
|
||||
`;
|
||||
|
||||
export const INIT_MCP_CLIENT = `
|
||||
CREATE TABLE IF NOT EXISTS mcp_client (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
client_id TEXT NOT NULL UNIQUE,
|
||||
client_secret TEXT,
|
||||
client_name TEXT NOT NULL,
|
||||
redirect_uris TEXT NOT NULL,
|
||||
grant_types TEXT,
|
||||
response_types TEXT,
|
||||
scope TEXT,
|
||||
created_at TEXT NOT NULL DEFAULT (${UTC_NOW})
|
||||
);
|
||||
`;
|
||||
|
||||
export const INIT_MCP_AUTH_CODE = `
|
||||
CREATE TABLE IF NOT EXISTS mcp_auth_code (
|
||||
code TEXT PRIMARY KEY,
|
||||
client_id TEXT NOT NULL,
|
||||
google_auth_id INTEGER NOT NULL,
|
||||
redirect_uri TEXT NOT NULL,
|
||||
code_challenge TEXT NOT NULL,
|
||||
code_challenge_method TEXT NOT NULL,
|
||||
scope TEXT,
|
||||
expires_at TEXT NOT NULL,
|
||||
used INTEGER NOT NULL DEFAULT 0,
|
||||
created_at TEXT NOT NULL DEFAULT (${UTC_NOW}),
|
||||
FOREIGN KEY (google_auth_id) REFERENCES google_auth(id) ON DELETE CASCADE
|
||||
);
|
||||
`;
|
||||
|
||||
export const INIT_MCP_TOKEN = `
|
||||
CREATE TABLE IF NOT EXISTS mcp_token (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
refresh_token TEXT NOT NULL UNIQUE,
|
||||
client_id TEXT NOT NULL,
|
||||
google_auth_id INTEGER NOT NULL,
|
||||
scope TEXT,
|
||||
expires_at TEXT NOT NULL,
|
||||
revoked INTEGER NOT NULL DEFAULT 0,
|
||||
created_at TEXT NOT NULL DEFAULT (${UTC_NOW}),
|
||||
FOREIGN KEY (google_auth_id) REFERENCES google_auth(id) ON DELETE CASCADE
|
||||
);
|
||||
`;
|
||||
|
||||
/** DDL scripts in dependency order for SQLite schema initialization. */
|
||||
export const SCHEMA_SCRIPTS = [
|
||||
INIT_META,
|
||||
@@ -909,4 +953,7 @@ export const SCHEMA_SCRIPTS = [
|
||||
INIT_PAYMENT,
|
||||
INIT_SHIPPING,
|
||||
INIT_PACKAGE,
|
||||
INIT_MCP_CLIENT,
|
||||
INIT_MCP_AUTH_CODE,
|
||||
INIT_MCP_TOKEN,
|
||||
];
|
||||
|
||||
16
src/store/mcp/consumeAuthCode.js
Normal file
16
src/store/mcp/consumeAuthCode.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import { CONSUME_AUTH_CODE as MSSQL_CONSUME } from '../../providers/mssql/queries/mcp.js';
|
||||
import { CONSUME_AUTH_CODE as SQLITE_CONSUME } from '../../providers/sqlite3/queries/mcp.js';
|
||||
|
||||
export default function consumeAuthCode(provider) {
|
||||
const sql = provider.dialect === 'sqlite3' ? SQLITE_CONSUME : MSSQL_CONSUME;
|
||||
/**
|
||||
* Marks an authorization code as used.
|
||||
*
|
||||
* @param {string} code - The authorization code to mark used.
|
||||
* @returns {Promise<boolean>} True once marked.
|
||||
*/
|
||||
return async (code) => {
|
||||
await provider.query(sql, { code });
|
||||
return true;
|
||||
};
|
||||
}
|
||||
43
src/store/mcp/createAuthCode.js
Normal file
43
src/store/mcp/createAuthCode.js
Normal file
@@ -0,0 +1,43 @@
|
||||
import { INSERT_AUTH_CODE as MSSQL_INSERT } from '../../providers/mssql/queries/mcp.js';
|
||||
import { INSERT_AUTH_CODE as SQLITE_INSERT } from '../../providers/sqlite3/queries/mcp.js';
|
||||
|
||||
export default function createAuthCode(provider) {
|
||||
const sql = provider.dialect === 'sqlite3' ? SQLITE_INSERT : MSSQL_INSERT;
|
||||
/**
|
||||
* Stores an OAuth 2.1 authorization code with PKCE parameters.
|
||||
*
|
||||
* @param {object} input
|
||||
* @param {string} input.code - The generated authorization code.
|
||||
* @param {string} input.clientId - The client ID.
|
||||
* @param {number} input.googleAuthId - The ID of the authenticated user.
|
||||
* @param {string} input.redirectUri - The redirect URI used in the authorize request.
|
||||
* @param {string} input.codeChallenge - The PKCE code challenge.
|
||||
* @param {string} input.codeChallengeMethod - The PKCE method (e.g. S256).
|
||||
* @param {string|null} [input.scope] - Granted scope.
|
||||
* @param {string|Date} input.expiresAt - Expiration ISO string or Date.
|
||||
* @returns {Promise<string>} The authorization code.
|
||||
*/
|
||||
return async ({
|
||||
code,
|
||||
clientId,
|
||||
googleAuthId,
|
||||
redirectUri,
|
||||
codeChallenge,
|
||||
codeChallengeMethod = 'S256',
|
||||
scope = 'mcp:tools',
|
||||
expiresAt,
|
||||
}) => {
|
||||
const exp = expiresAt instanceof Date ? expiresAt.toISOString() : expiresAt;
|
||||
await provider.query(sql, {
|
||||
code,
|
||||
clientId,
|
||||
googleAuthId,
|
||||
redirectUri,
|
||||
codeChallenge,
|
||||
codeChallengeMethod,
|
||||
scope,
|
||||
expiresAt: exp,
|
||||
});
|
||||
return code;
|
||||
};
|
||||
}
|
||||
34
src/store/mcp/createRefreshToken.js
Normal file
34
src/store/mcp/createRefreshToken.js
Normal file
@@ -0,0 +1,34 @@
|
||||
import { INSERT_REFRESH_TOKEN as MSSQL_INSERT } from '../../providers/mssql/queries/mcp.js';
|
||||
import { INSERT_REFRESH_TOKEN as SQLITE_INSERT } from '../../providers/sqlite3/queries/mcp.js';
|
||||
|
||||
export default function createRefreshToken(provider) {
|
||||
const sql = provider.dialect === 'sqlite3' ? SQLITE_INSERT : MSSQL_INSERT;
|
||||
/**
|
||||
* Stores an OAuth 2.1 refresh token.
|
||||
*
|
||||
* @param {object} input
|
||||
* @param {string} input.refreshToken - The refresh token string.
|
||||
* @param {string} input.clientId - The client ID.
|
||||
* @param {number} input.googleAuthId - The user ID.
|
||||
* @param {string|null} [input.scope] - Scopes granted.
|
||||
* @param {string|Date} input.expiresAt - Expiration Date or ISO string.
|
||||
* @returns {Promise<object>} Created token record or id.
|
||||
*/
|
||||
return async ({
|
||||
refreshToken,
|
||||
clientId,
|
||||
googleAuthId,
|
||||
scope = 'mcp:tools',
|
||||
expiresAt,
|
||||
}) => {
|
||||
const exp = expiresAt instanceof Date ? expiresAt.toISOString() : expiresAt;
|
||||
const result = await provider.query(sql, {
|
||||
refreshToken,
|
||||
clientId,
|
||||
googleAuthId,
|
||||
scope,
|
||||
expiresAt: exp,
|
||||
});
|
||||
return result.rows[0]?.id ?? result.insertId;
|
||||
};
|
||||
}
|
||||
16
src/store/mcp/deleteClient.js
Normal file
16
src/store/mcp/deleteClient.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import { DELETE_CLIENT as MSSQL_DELETE } from '../../providers/mssql/queries/mcp.js';
|
||||
import { DELETE_CLIENT as SQLITE_DELETE } from '../../providers/sqlite3/queries/mcp.js';
|
||||
|
||||
export default function deleteClient(provider) {
|
||||
const sql = provider.dialect === 'sqlite3' ? SQLITE_DELETE : MSSQL_DELETE;
|
||||
/**
|
||||
* Deletes a registered MCP client by client ID.
|
||||
*
|
||||
* @param {string} clientId - The client identifier to delete.
|
||||
* @returns {Promise<boolean>} True if deleted.
|
||||
*/
|
||||
return async (clientId) => {
|
||||
await provider.query(sql, { clientId });
|
||||
return true;
|
||||
};
|
||||
}
|
||||
16
src/store/mcp/getAuthCode.js
Normal file
16
src/store/mcp/getAuthCode.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import { GET_AUTH_CODE as MSSQL_GET } from '../../providers/mssql/queries/mcp.js';
|
||||
import { GET_AUTH_CODE as SQLITE_GET } from '../../providers/sqlite3/queries/mcp.js';
|
||||
|
||||
export default function getAuthCode(provider) {
|
||||
const sql = provider.dialect === 'sqlite3' ? SQLITE_GET : MSSQL_GET;
|
||||
/**
|
||||
* Retrieves an authorization code record.
|
||||
*
|
||||
* @param {string} code - The authorization code.
|
||||
* @returns {Promise<object|null>} The auth code record or null.
|
||||
*/
|
||||
return async (code) => {
|
||||
const result = await provider.query(sql, { code });
|
||||
return result.rows[0] ?? null;
|
||||
};
|
||||
}
|
||||
16
src/store/mcp/getClient.js
Normal file
16
src/store/mcp/getClient.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import { GET_CLIENT as MSSQL_GET } from '../../providers/mssql/queries/mcp.js';
|
||||
import { GET_CLIENT as SQLITE_GET } from '../../providers/sqlite3/queries/mcp.js';
|
||||
|
||||
export default function getClient(provider) {
|
||||
const sql = provider.dialect === 'sqlite3' ? SQLITE_GET : MSSQL_GET;
|
||||
/**
|
||||
* Retrieves an MCP client by client ID.
|
||||
*
|
||||
* @param {string} clientId - The client identifier.
|
||||
* @returns {Promise<object|null>} The client record or null if not found.
|
||||
*/
|
||||
return async (clientId) => {
|
||||
const result = await provider.query(sql, { clientId });
|
||||
return result.rows[0] ?? null;
|
||||
};
|
||||
}
|
||||
16
src/store/mcp/getRefreshToken.js
Normal file
16
src/store/mcp/getRefreshToken.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import { GET_REFRESH_TOKEN as MSSQL_GET } from '../../providers/mssql/queries/mcp.js';
|
||||
import { GET_REFRESH_TOKEN as SQLITE_GET } from '../../providers/sqlite3/queries/mcp.js';
|
||||
|
||||
export default function getRefreshToken(provider) {
|
||||
const sql = provider.dialect === 'sqlite3' ? SQLITE_GET : MSSQL_GET;
|
||||
/**
|
||||
* Retrieves an OAuth 2.1 refresh token.
|
||||
*
|
||||
* @param {string} refreshToken - The refresh token.
|
||||
* @returns {Promise<object|null>} The refresh token record or null.
|
||||
*/
|
||||
return async (refreshToken) => {
|
||||
const result = await provider.query(sql, { refreshToken });
|
||||
return result.rows[0] ?? null;
|
||||
};
|
||||
}
|
||||
15
src/store/mcp/listClients.js
Normal file
15
src/store/mcp/listClients.js
Normal file
@@ -0,0 +1,15 @@
|
||||
import { LIST_CLIENTS as MSSQL_LIST } from '../../providers/mssql/queries/mcp.js';
|
||||
import { LIST_CLIENTS as SQLITE_LIST } from '../../providers/sqlite3/queries/mcp.js';
|
||||
|
||||
export default function listClients(provider) {
|
||||
const sql = provider.dialect === 'sqlite3' ? SQLITE_LIST : MSSQL_LIST;
|
||||
/**
|
||||
* Lists all registered MCP clients.
|
||||
*
|
||||
* @returns {Promise<Array<object>>} List of client records.
|
||||
*/
|
||||
return async () => {
|
||||
const result = await provider.query(sql);
|
||||
return result.rows ?? [];
|
||||
};
|
||||
}
|
||||
55
src/store/mcp/registerClient.js
Normal file
55
src/store/mcp/registerClient.js
Normal file
@@ -0,0 +1,55 @@
|
||||
import { INSERT_CLIENT as MSSQL_INSERT } from '../../providers/mssql/queries/mcp.js';
|
||||
import { INSERT_CLIENT as SQLITE_INSERT } from '../../providers/sqlite3/queries/mcp.js';
|
||||
|
||||
export default function registerClient(provider) {
|
||||
const sql = provider.dialect === 'sqlite3' ? SQLITE_INSERT : MSSQL_INSERT;
|
||||
/**
|
||||
* Registers a new OAuth MCP client.
|
||||
*
|
||||
* @param {object} input
|
||||
* @param {string} input.clientId - Unique client identifier.
|
||||
* @param {string|null} [input.clientSecret] - Optional client secret for confidential clients.
|
||||
* @param {string} input.clientName - Human-readable client name.
|
||||
* @param {string|Array<string>} input.redirectUris - JSON string or array of redirect URIs.
|
||||
* @param {string|Array<string>} [input.grantTypes] - Supported grant types.
|
||||
* @param {string|Array<string>} [input.responseTypes] - Supported response types.
|
||||
* @param {string|null} [input.scope] - Allowed scope.
|
||||
* @returns {Promise<object>} The created client record.
|
||||
*/
|
||||
return async ({
|
||||
clientId,
|
||||
clientSecret = null,
|
||||
clientName,
|
||||
redirectUris,
|
||||
grantTypes = 'authorization_code,refresh_token',
|
||||
responseTypes = 'code',
|
||||
scope = 'mcp:tools',
|
||||
}) => {
|
||||
const urisStr = Array.isArray(redirectUris) ? JSON.stringify(redirectUris) : redirectUris;
|
||||
const grantsStr = Array.isArray(grantTypes) ? grantTypes.join(',') : grantTypes;
|
||||
const resTypesStr = Array.isArray(responseTypes) ? responseTypes.join(',') : responseTypes;
|
||||
|
||||
const result = await provider.query(sql, {
|
||||
clientId,
|
||||
clientSecret,
|
||||
clientName,
|
||||
redirectUris: urisStr,
|
||||
grantTypes: grantsStr,
|
||||
responseTypes: resTypesStr,
|
||||
scope,
|
||||
});
|
||||
|
||||
const row = result.rows[0];
|
||||
return {
|
||||
id: row?.id ?? result.insertId,
|
||||
clientId: row?.clientId ?? row?.client_id ?? clientId,
|
||||
clientSecret: row?.clientSecret ?? row?.client_secret ?? clientSecret,
|
||||
clientName: row?.clientName ?? row?.client_name ?? clientName,
|
||||
redirectUris: row?.redirectUris ?? row?.redirect_uris ?? urisStr,
|
||||
grantTypes: row?.grantTypes ?? row?.grant_types ?? grantsStr,
|
||||
responseTypes: row?.responseTypes ?? row?.response_types ?? resTypesStr,
|
||||
scope: row?.scope ?? scope,
|
||||
createdAt: row?.createdAt ?? row?.created_at,
|
||||
};
|
||||
};
|
||||
}
|
||||
16
src/store/mcp/revokeRefreshToken.js
Normal file
16
src/store/mcp/revokeRefreshToken.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import { REVOKE_REFRESH_TOKEN as MSSQL_REVOKE } from '../../providers/mssql/queries/mcp.js';
|
||||
import { REVOKE_REFRESH_TOKEN as SQLITE_REVOKE } from '../../providers/sqlite3/queries/mcp.js';
|
||||
|
||||
export default function revokeRefreshToken(provider) {
|
||||
const sql = provider.dialect === 'sqlite3' ? SQLITE_REVOKE : MSSQL_REVOKE;
|
||||
/**
|
||||
* Revokes an OAuth 2.1 refresh token.
|
||||
*
|
||||
* @param {string} refreshToken - The refresh token to revoke.
|
||||
* @returns {Promise<boolean>} True once revoked.
|
||||
*/
|
||||
return async (refreshToken) => {
|
||||
await provider.query(sql, { refreshToken });
|
||||
return true;
|
||||
};
|
||||
}
|
||||
@@ -34,7 +34,8 @@ export default function peekNumber(provider) {
|
||||
*/
|
||||
return async (companyId, type, tx) => {
|
||||
const doPeek = async (runner) => {
|
||||
await runner.query(ensureSql, { companyId, type });
|
||||
const prefix = type === 'shipment' ? '-L-' : '';
|
||||
await runner.query(ensureSql, { companyId, type, prefix });
|
||||
const res = await runner.query(getSql, { companyId, type });
|
||||
const row = res.rows[0];
|
||||
if (!row) {
|
||||
|
||||
Reference in New Issue
Block a user