• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

cofacts / rumors-api / 25983239302

17 May 2026 06:14AM UTC coverage: 78.834% (-3.4%) from 82.22%
25983239302

Pull #389

github

web-flow
Merge c765e72a1 into 59e4e0b39
Pull Request #389: feat(mcp): add remote MCP server with OAuth 2.1 + PKCE

893 of 1212 branches covered (73.68%)

Branch coverage included in aggregate %.

18 of 110 new or added lines in 6 files covered. (16.36%)

8 existing lines in 1 file now uncovered.

1744 of 2133 relevant lines covered (81.76%)

16.38 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

5.71
/src/mcpRouter.js
1
import Router from 'koa-router';
2
import { isAllowedCallbackUrl } from './auth';
3
import { handleMcpRequest } from './mcpServer';
4
import { verifyJWT, signShortLivedJWT, TOKEN_USE_AUTH_CODE } from './lib/jwt';
5

6
const PROVIDERS = [
1✔
7
  ['google', 'Google', 'GOOGLE_CLIENT_ID'],
8
  ['facebook', 'Facebook', 'FACEBOOK_APP_ID'],
9
  ['github', 'GitHub', 'GITHUB_CLIENT_ID'],
10
  ['instagram', 'Instagram', 'INSTAGRAM_CLIENT_ID'],
11
];
12

13
const mcpRouter = new Router();
1✔
14

15
// MCP protocol endpoint
16
mcpRouter.all('/', handleMcpRequest);
1✔
17

18
// OAuth authorization: provider selection page
19
mcpRouter.get('/login', (ctx) => {
1✔
20
  const {
21
    response_type,
22
    redirect_uri,
23
    state,
24
    code_challenge,
25
    code_challenge_method,
NEW
26
  } = ctx.query;
×
27

NEW
28
  if (response_type !== 'code' || !redirect_uri) {
×
NEW
29
    ctx.status = 400;
×
NEW
30
    ctx.body = { error: 'invalid_request' };
×
NEW
31
    return;
×
32
  }
33

34
  let redirectTo, stateParam;
35

NEW
36
  if (code_challenge) {
×
37
    // PKCE flow — any valid HTTP/HTTPS redirect_uri allowed
NEW
38
    if (code_challenge_method !== 'S256') {
×
NEW
39
      ctx.status = 400;
×
NEW
40
      ctx.body = {
×
41
        error: 'invalid_request',
42
        error_description: 'Only S256 code_challenge_method is supported',
43
      };
NEW
44
      return;
×
45
    }
NEW
46
    try {
×
NEW
47
      const u = new URL(redirect_uri);
×
48
      const isLoopback =
NEW
49
        u.hostname === 'localhost' || u.hostname === '127.0.0.1';
×
NEW
50
      if (u.protocol !== 'https:' && !(u.protocol === 'http:' && isLoopback))
×
NEW
51
        throw new Error();
×
52
    } catch {
NEW
53
      ctx.status = 400;
×
NEW
54
      ctx.body = {
×
55
        error: 'invalid_request',
56
        error_description: 'invalid redirect_uri',
57
      };
NEW
58
      return;
×
59
    }
60

NEW
61
    const mcpState = Buffer.from(
×
62
      JSON.stringify({ r: redirect_uri, cc: code_challenge, os: state || '' })
×
63
    ).toString('base64url');
64

NEW
65
    const origin = process.env.API_ORIGIN || ctx.request.origin;
×
NEW
66
    redirectTo = `${origin}/mcp/callback`;
×
NEW
67
    stateParam = `&state=${encodeURIComponent(mcpState)}`;
×
68
  } else {
69
    // 1st party flow — redirect_uri must be whitelisted
NEW
70
    if (!isAllowedCallbackUrl(redirect_uri)) {
×
NEW
71
      ctx.status = 400;
×
NEW
72
      ctx.body = {
×
73
        error: 'invalid_request',
74
        error_description: 'redirect_uri not allowed',
75
      };
NEW
76
      return;
×
77
    }
NEW
78
    redirectTo = redirect_uri;
×
NEW
79
    stateParam = state ? `&state=${encodeURIComponent(state)}` : '';
×
80
  }
81

NEW
82
  const buttons = PROVIDERS.filter(([, , envKey]) => process.env[envKey])
×
83
    .map(
84
      ([id, label]) =>
NEW
85
        `<a href="/login/${id}?redirect_to=${encodeURIComponent(
×
86
          redirectTo
87
        )}${stateParam}" style="display:block;margin:8px 0;padding:12px 20px;background:#4285f4;color:#fff;text-decoration:none;border-radius:4px;text-align:center;font-family:sans-serif">${label}</a>`
88
    )
89
    .join('\n');
90

NEW
91
  ctx.type = 'text/html';
×
NEW
92
  ctx.body = `<!DOCTYPE html>
×
93
<html>
94
<head><meta charset="utf-8"><title>Log in to Cofacts</title></head>
95
<body style="font-family:sans-serif;max-width:360px;margin:80px auto;padding:20px">
96
  <h2 style="margin-bottom:24px">Log in to Cofacts</h2>
97
  ${buttons}
98
</body>
99
</html>`;
100
});
101

102
// Dynamic client registration (RFC 7591)
103
mcpRouter.post('/register', (ctx) => {
1✔
NEW
104
  const { redirect_uris = [], client_name } = ctx.request.body || {};
×
NEW
105
  ctx.status = 201;
×
NEW
106
  ctx.body = {
×
107
    client_id: 'mcp-public-client',
108
    client_id_issued_at: Math.floor(Date.now() / 1000),
109
    redirect_uris,
110
    client_name: client_name || 'MCP Client',
×
111
    grant_types: ['authorization_code'],
112
    response_types: ['code'],
113
    token_endpoint_auth_method: 'none',
114
  };
115
});
116

117
// PKCE bounce: verifies incoming auth code, re-signs with code_challenge claim,
118
// then redirects to the actual MCP client callback URL.
119
mcpRouter.get('/callback', async (ctx) => {
1✔
NEW
120
  const { code, state } = ctx.query;
×
121

122
  let mcpState;
NEW
123
  try {
×
NEW
124
    mcpState = JSON.parse(Buffer.from(state, 'base64url').toString('utf-8'));
×
125
  } catch {
NEW
126
    ctx.status = 400;
×
NEW
127
    ctx.body = { error: 'invalid_state' };
×
NEW
128
    return;
×
129
  }
130

NEW
131
  const { r: actualCb, cc: codeChallenge, os: originalState } = mcpState;
×
132

NEW
133
  try {
×
NEW
134
    const u = new URL(actualCb);
×
NEW
135
    const isLoopback = u.hostname === 'localhost' || u.hostname === '127.0.0.1';
×
NEW
136
    if (u.protocol !== 'https:' && !(u.protocol === 'http:' && isLoopback))
×
NEW
137
      throw new Error();
×
138
  } catch {
NEW
139
    ctx.status = 400;
×
NEW
140
    ctx.body = { error: 'invalid_callback' };
×
NEW
141
    return;
×
142
  }
143

144
  let payload;
NEW
145
  try {
×
NEW
146
    payload = await verifyJWT(code, { expectedUse: TOKEN_USE_AUTH_CODE });
×
147
  } catch {
NEW
148
    ctx.status = 401;
×
NEW
149
    ctx.body = { error: 'invalid_code' };
×
NEW
150
    return;
×
151
  }
152

NEW
153
  const newCode = await signShortLivedJWT(payload.sub, { codeChallenge });
×
NEW
154
  const redirectUrl = new URL(actualCb);
×
NEW
155
  redirectUrl.searchParams.set('code', newCode);
×
NEW
156
  if (originalState) redirectUrl.searchParams.set('state', originalState);
×
NEW
157
  ctx.redirect(redirectUrl.href);
×
158
});
159

160
export default mcpRouter;
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc