@nooticr/mcp 1.26.74 → 1.26.76

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/auth.js CHANGED
@@ -225,17 +225,35 @@ export function createManagementTokenProvider(auth) {
225
225
  /**
226
226
  * Builds a TokenProvider for one caller's own API key — the headless path
227
227
  * through HTTP mode, where the bearer the client presented *is* the nooticr
228
- * credential. Deliberately not `createHttpTokenProvider`: that one falls back
229
- * to the operator's environment and credentials file, which would answer one
230
- * tenant's call with another's account.
228
+ * credential. Never the operator's environment or credentials file, which would
229
+ * answer one tenant's call with another's account.
231
230
  */
232
231
  export function createApiKeyTokenProvider(apiKey) {
233
232
  return { getAccessToken: async () => apiKey };
234
233
  }
235
- /** Builds a TokenProvider for HTTP mode: env > api key > file > per-session tokens. */
236
- export function createHttpTokenProvider(auth, session) {
234
+ /**
235
+ * Builds a TokenProvider for one OAuth session in HTTP mode: that session's
236
+ * nooticr tokens and nothing else. The environment and the credentials file
237
+ * belong to whoever runs the server; resolving them here answered every
238
+ * signed-in user as the operator. A refresh updates the session in place, so
239
+ * the next request from the same login carries the new token, and nothing is
240
+ * written to the operator's credentials file.
241
+ */
242
+ export function createSessionTokenProvider(baseUrl, session) {
237
243
  return {
238
- getAccessToken: async () => auth.getAccessToken(session),
239
- onUnauthorized: async () => auth.onUnauthorized(session),
244
+ getAccessToken: async () => session.nooticrAccessToken || undefined,
245
+ onUnauthorized: async () => {
246
+ if (!session.nooticrRefreshToken)
247
+ return false;
248
+ try {
249
+ const next = await NooticrClient.refreshSession(baseUrl, session.nooticrRefreshToken);
250
+ session.nooticrAccessToken = next.accessToken;
251
+ session.nooticrRefreshToken = next.refreshToken ?? session.nooticrRefreshToken;
252
+ return true;
253
+ }
254
+ catch {
255
+ return false;
256
+ }
257
+ },
240
258
  };
241
259
  }
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ import { randomUUID } from "node:crypto";
14
14
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
15
15
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
16
16
  import { getBaseUrl, getPublicUrl, getPort, getCredentialsFile, getTransportMode, DEFAULT_PORT, } from "./config.js";
17
- import { AuthManager, NooticrAuthError, createApiKeyTokenProvider, createHttpTokenProvider, createManagementTokenProvider, createStdioTokenProvider, } from "./auth.js";
17
+ import { AuthManager, NooticrAuthError, createApiKeyTokenProvider, createSessionTokenProvider, createManagementTokenProvider, createStdioTokenProvider, } from "./auth.js";
18
18
  import { forwardedHeaders, forwardedSession, looksLikeApiKey } from "./shared/api-key.js";
19
19
  import { NooticrClient, NooticrError } from "./nooticr.js";
20
20
  import { OAuthManager, SCOPES } from "./oauth.js";
@@ -227,9 +227,6 @@ export async function runHttp(port, publicUrl) {
227
227
  client: new NooticrClient(baseUrl, {
228
228
  getAccessToken: async () => undefined,
229
229
  }),
230
- onSession: async (session) => {
231
- await auth.persistSession(session);
232
- },
233
230
  });
234
231
  const state = {
235
232
  oauth,
@@ -239,9 +236,9 @@ export async function runHttp(port, publicUrl) {
239
236
  serverFactory: () => createMcpServer((extra) => {
240
237
  const bearer = extra.authInfo?.token;
241
238
  // A caller who presented their own API key is answered as themselves.
242
- // Falling through to `createHttpTokenProvider` here would resolve the
243
- // *operator's* environment or credentials file instead — one tenant's
244
- // request served from another's account.
239
+ // No branch below falls back to the *operator's* environment or
240
+ // credentials file: that would serve one tenant's request from
241
+ // another's account.
245
242
  if (looksLikeApiKey(bearer)) {
246
243
  return new NooticrClient(baseUrl, createApiKeyTokenProvider(bearer));
247
244
  }
@@ -250,15 +247,13 @@ export async function runHttp(port, publicUrl) {
250
247
  if (forwarded) {
251
248
  return new NooticrClient(baseUrl, createApiKeyTokenProvider(forwarded.token), undefined, forwardedHeaders(forwarded));
252
249
  }
250
+ // An OAuth login of this server: only that session's own tokens.
253
251
  const session = bearer
254
252
  ? oauth.verifyToken(bearer)
255
253
  : undefined;
256
- return new NooticrClient(baseUrl, createHttpTokenProvider(auth, session
257
- ? {
258
- accessToken: session.nooticrAccessToken,
259
- refreshToken: session.nooticrRefreshToken,
260
- }
261
- : undefined));
254
+ return new NooticrClient(baseUrl, session
255
+ ? createSessionTokenProvider(baseUrl, session)
256
+ : { getAccessToken: async () => undefined });
262
257
  }),
263
258
  };
264
259
  const server = http.createServer((req, res) => {
@@ -36,7 +36,7 @@ import { registerAmazonTools } from "./amazon.js";
36
36
  import { registerMarketplaceTools } from "./marketplace.js";
37
37
  import { DEFAULT_NOOTICR_SETTINGS, DEFAULT_SOCIAL_PLATFORMS, MemorySettingsStore, SETTINGS_READ_TOOL, SETTINGS_UPDATE_TOOL, settingsOwnerFromAuth, } from "./settings.js";
38
38
  /** Current MCP server version — bumped on every deploy for traceability. */
39
- export const MCP_SERVER_VERSION = "1.26.74";
39
+ export const MCP_SERVER_VERSION = "1.26.76";
40
40
  /** MCP Apps extension identifier */
41
41
  const UI_EXTENSION = "io.modelcontextprotocol/ui";
42
42
  /** MIME type for MCP Apps HTML resources */
@@ -2066,7 +2066,8 @@ export const NOOTICR_UI_TEMPLATE = `<!DOCTYPE html>
2066
2066
  var GROUP_PEEK=4;
2067
2067
  /** Groups the reader has opened, by post URL, kept across a filter or sort. */
2068
2068
  var expandedGroups={};
2069
- // Posters the reader has asked to play, keyed like expandedGroups.
2069
+ // The poster the reader asked to play, keyed like expandedGroups. At most
2070
+ // one key: see the [data-mplay] handler.
2070
2071
  var expandedMedia={};
2071
2072
 
2072
2073
  /** Reach of the post a comment sits under — how many people saw it. */
@@ -2227,11 +2228,16 @@ export const NOOTICR_UI_TEMPLATE = `<!DOCTYPE html>
2227
2228
  if(mplay&&(monitorState||spokenState)){
2228
2229
  var mkey=mplay.getAttribute("data-mplay")||"";
2229
2230
  if(mkey&&mplay.className.indexOf("is-playable")>=0){
2231
+ // One post plays at a time. Every expanded stage is redrawn with
2232
+ // autoplay, so a second poster click used to restart the first video
2233
+ // under the new one, and only the first stage in the page was
2234
+ // registered below, so neither paused the other (#253).
2235
+ expandedMedia={};
2230
2236
  expandedMedia[mkey]=true;
2231
2237
  renderMentions();
2232
- // renderMentions draws outside the top-level render, so anything it
2233
- // just wrote has nothing bound to it. initPlayers resets first, so
2234
- // calling it again here is safe.
2238
+ // Nothing in a mention view is a bound player today (no .mp, .au or
2239
+ // poster fallback), so this only resets the registries before the
2240
+ // stage is registered; it binds anything a later view adds.
2235
2241
  initPlayers();
2236
2242
  var v=document.querySelector(".mgroup-stage video");
2237
2243
  if(v){registerAudio(v);stopAllPlayers();}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@nooticr/mcp",
3
3
  "mcpName": "com.nooticr/mcp",
4
- "version": "1.26.74",
4
+ "version": "1.26.76",
5
5
  "description": "MCP server for nooticr - fetch, discover and understand TikTok/Instagram/YouTube/X/Reddit/LinkedIn/Weibo posts with AI (media metadata, niche discovery, brand monitoring, hook & viral analysis)",
6
6
  "homepage": "https://mcp.nooticr.com",
7
7
  "repository": {