• 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

0.0
/src/mcpServer.js
1
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
3
import { z } from 'zod';
4
import { graphql } from 'graphql';
5
import schema from './graphql/schema';
6
import contextFactory from './contextFactory';
7
import { verifyJWT, TOKEN_USE_ACCESS } from './lib/jwt';
8

9
export async function handleMcpRequest(ctx) {
NEW
10
  const authHeader = ctx.get('authorization');
×
11

NEW
12
  if (!authHeader?.startsWith('Bearer ')) {
×
NEW
13
    ctx.status = 401;
×
NEW
14
    ctx.set('WWW-Authenticate', 'Bearer');
×
NEW
15
    ctx.body = { error: 'unauthorized' };
×
NEW
16
    return;
×
17
  }
18

19
  let payload;
NEW
20
  try {
×
NEW
21
    payload = await verifyJWT(authHeader.slice('Bearer '.length), {
×
22
      expectedUse: TOKEN_USE_ACCESS,
23
    });
24
  } catch {
NEW
25
    ctx.status = 401;
×
NEW
26
    ctx.set('WWW-Authenticate', 'Bearer error="invalid_token"');
×
NEW
27
    ctx.body = { error: 'invalid_token' };
×
NEW
28
    return;
×
29
  }
30

NEW
31
  const gqlContext = await contextFactory({
×
32
    ctx: {
33
      appId: 'MCP',
34
      query: {},
35
      state: { user: { userId: payload.sub } },
36
    },
37
  });
38

NEW
39
  const server = new McpServer({ name: 'cofacts-api', version: '1.0.0' });
×
40

NEW
41
  server.tool(
×
42
    'execute_graphql',
43
    'Execute a GraphQL query or mutation against the Cofacts API',
44
    {
45
      query: z.string().describe('GraphQL query or mutation string'),
46
      variables: z.record(z.unknown()).optional().describe('GraphQL variables'),
47
      operationName: z.string().optional().describe('Operation name'),
48
    },
49
    async ({ query, variables, operationName }) => {
NEW
50
      const result = await graphql({
×
51
        schema,
52
        source: query,
53
        variableValues: variables,
54
        operationName,
55
        contextValue: gqlContext,
56
      });
NEW
57
      return { content: [{ type: 'text', text: JSON.stringify(result) }] };
×
58
    }
59
  );
60

NEW
61
  const transport = new StreamableHTTPServerTransport({
×
62
    sessionIdGenerator: undefined,
63
  });
64

NEW
65
  await server.connect(transport);
×
66

NEW
67
  ctx.respond = false;
×
NEW
68
  await transport.handleRequest(ctx.req, ctx.res, ctx.request.body);
×
69
}
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